diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index fdcc8eaf..00000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: test -on: [push, pull_request] -jobs: - test: - timeout-minutes: 3 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: buckaroobanzay/mtt@main - with: - test_mode: game - mapgen: v7 - additional_config: | - secure.trusted_mods = mtt, libox - secure.http_mods = sbz_logic_devices \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index e69de29b..4fb51292 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,39 @@ +[submodule "mods/hotbar_switching"] + path = mods/hotbar_switching + url = https://github.com/TheEt1234/hotbar_switching +[submodule "luanti_lsp_definitions"] + path = luanti_lsp_definitions + url = https://github.com/corpserot/luanti_lsp_definitions/ +[submodule "mods/unified_inventory_plus"] + path = mods/unified_inventory_plus + url = https://github.com/mt-mods/unified_inventory_plus +[submodule "mods/libox"] + path = mods/libox + url = https://github.com/TheEt1234/libox +[submodule "mods/fakelib"] + path = mods/fakelib + url = https://github.com/OgelGames/fakelib +[submodule "mods/player_api"] + path = mods/player_api + url = https://github.com/minetest-game/player_api +[submodule "mods/sethome"] + path = mods/sethome + url = https://github.com/minetest-game/sethome +[submodule "mods/modlib"] + path = mods/modlib + url = https://github.com/appgurueu/modlib +[submodule "mods/visible_wielditem"] + path = mods/visible_wielditem + url = https://github.com/appgurueu/visible_wielditem +[submodule "mods/controls"] + path = mods/controls + url = https://github.com/mt-mods/controls +[submodule "mods/minetest-fmod"] + path = mods/minetest-fmod + url = https://github.com/fluxionary/minetest-fmod +[submodule "mods/minetest-futil"] + path = mods/minetest-futil + url = https://github.com/fluxionary/minetest-futil +[submodule "mods/stubes"] + path = mods/stubes + url = https://github.com/TheEt1234/luanti-stubes diff --git a/.luarc.json b/.luarc.json index 7a95a52e..987b422d 100644 --- a/.luarc.json +++ b/.luarc.json @@ -1,6 +1,6 @@ { "runtime.version": "LuaJIT", "completion.autoRequire": false, - "workspace.library": [ "types/core.d.lua" ], + "workspace.library": [ "luanti_lsp_definitions/" ], "diagnostic.disable": [ "lowercase-global" ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index fc412d22..98e473c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,15 @@ -## Release 38 +# Release 40 +- Development resumed again! + - We are still looking for contributors, if you enjoy SBZ and want to contribute, please do so! +- **Fixed various bugs** - main point of the release +- Added new background music +- Reworked autocrafters (They now require a crafting processor, old autocrafters will remain functioning with 1 craft/s speed) +- Made robotic arms and annihilators stackable, and removed wear from them +- Made manual crafters slightly cheaper +- Re-worded some quests to be more..... readable + + +# Release 38 ### The main stuff: - Large Liquid Storage - another multiblock, you won't guess what it does - Fixed meteorite attractor entities sometimes just multiplying @@ -29,7 +40,7 @@ - In the various lag statues, switched away from using `core.get_us_time`, and instead using `os.clock`, i don't know if this is actually better -Release 37 - sorry for the long wait :) - the "Oh UI looks decent now... and what's that? it's not a buggy mess anymore?" +# Release 37 - sorry for the long wait :) - the "Oh UI looks decent now... and what's that? it's not a buggy mess anymore?" - Fixed instatubes sometimes not working - Added bricks and wooden planks - Fixed one pixel of pebble being transparent (Yes, you weren't crazy!) @@ -53,7 +64,7 @@ Release 37 - sorry for the long wait :) - the "Oh UI looks decent now... and wha - Made drawers undiggable when filled with anything - Changed questbooks -Release 36 - "Oh no... there is stuff to fix actually, oops" Update +# Release 36 - "Oh no... there is stuff to fix actually, oops" Update - Fixed a bug with instatubes that made them behave really strangely, when a server restarted - Added the height limit for emitters back - Added a command that removes all pipeworks tube entities @@ -69,7 +80,7 @@ Release 36 - "Oh no... there is stuff to fix actually, oops" Update - Made antimatter platforms explody - Fixed one way instatube textures being in the wrong direction -Release 35 - "Optimization update" +# Release 35 - "Optimization update" - made pipes and tubes smaller (1/4th of a node => 3/16th of a node) - fixed some bugs - made fluid pipe look better @@ -86,7 +97,7 @@ Release 35 - "Optimization update" - Made copytool copy filter injector "exact match mode" -Release 34 +# Release 34 - Added a recipe to memory controller - Added an info page "Overflow Handling" - Added an "Items destroyed: " infotext to item voids @@ -94,7 +105,7 @@ Release 34 - Made burners store co2 - Fixed rare bugs with filter injectors -Release 33 +# Release 33 - Fixed a bug where filter injectors didn't work with one way tubes - Added one direction tube - A tube that accepts items from all directions, but sends them to only one direction @@ -141,11 +152,11 @@ Release 33 - Fixed the default editor's disk menu being weird sometimes -Release 32 +# Release 32 - Fix the bug with filter injectors crashing the game when directly outputting to accelerator tubes - Fix background music being at 0% volume by default -Release 31 +# Release 31 - Make the data disk description more accurate - they can only hold 20 kilobytes, not 1 megabyte - Re-worked how meteorite attractors/repulsors attract players when holding neutronium - You now no longer can move yourself, and experience zero gravity - This allows for making orbits if you are skilled @@ -176,11 +187,11 @@ Release 31 - Changed the behavior of filter injectors so that they don't try to push stuff out when the inventory they are trying to push stuff to is clearly full - Re-worked and fixed bugs with logic item transport -Release 30 +# Release 30 - Fixed crash bugs with jumpdrive and moving nodes with luacontrollers - Added lead shielding -Release 29 +# Release 29 - Notice: Releases may be really small like this one, or HUGE like release 28 - Started doing changelogs again - Added more "info sections" to the questbook, told people that you can hold right click to the core in the questbook @@ -195,7 +206,7 @@ Release 29 - Fixed a bug with ladders, where they refused to go in certain directions - Compressed images, lossy compressed the background to 200kb -Release 10 +# Release 10 - Completely recode energy system to use "Cosmic Joules" instead of "Global Power" - Pipe-based energy system - Removed quest "Global Power" @@ -206,7 +217,7 @@ Release 10 - added Starlight Collectors - Stopped doing changelogs -Release 9 +# Release 9 - New questline "Decorator" - Moved "Emitter Immitators" to the Decorator questline - New quest: "Photon Lamps" in Decorator Questline @@ -214,7 +225,7 @@ Release 9 - Fixed a bug reported by @theidealist (ty) - Secret quests now show up in the questbook as ??? -Release 8 +# Release 8 - Questbook now has questlines - Primitive Questbook API documentation added (see docs folder) - Questbook has new types of quests @@ -222,7 +233,7 @@ Release 8 - Added secret quest "Emptiness" - Improved indicators on quests -Release 7 +# Release 7 - fix the questbook reminders - player is now invisible, replaced by a white particle trail - removed players hand @@ -232,7 +243,7 @@ Release 7 - Add Dirt, Soil and Stone Nodes - Add 'Raw Emittrium' and 'Pebble' Items -Release 6 +# Release 6 - 'Emitter Immitator' now gives off twice as much light - Introduced the Quest Book instead of the Guide (which has been removed) - Ported 7 Quests over from the old quest system @@ -241,16 +252,16 @@ Release 6 - use /qb to get the questbook on old worlds - Custom hotbar textures -Release 5 +# Release 5 - Fixed an issue with infinite negative energy reported by @fgaz -Release 4 +# Release 4 - Fixed sneaking being insanely fast for no reason - Fixed bugs with Generator where it would give infinite energy / take infinite energy - Added conversion chamber craftitem - Added Organic Converter (not usable so far) -Release 3 +# Release 3 - Fixed power not getting removed when removing a running generator - Added space-like physics - Added a sound when placing a machine @@ -260,7 +271,7 @@ Release 3 - Added Simple Circuit and Retaining Circuit - Added Matter Plate -Release 2 +# Release 2 - Added Advanced Matter Extractor - Made Emitters display a message when clicked - Added "Advanced Extractors" Optional Quest / Achievement @@ -268,5 +279,5 @@ Release 2 - Added Simple Charge Generator - Added Emitter Immitator -Release 1 +# Release 1 - First release diff --git a/README.md b/README.md index 67957234..e62784fd 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,16 @@ Start from absolutely nothing in a skyblock world. Time is your resource. Get lo ## Playing There is an in-game Quest Book to get you started. Have fun!
-- **Please report bugs in the package threads, i will fix them ASAP.** +- **Please report bugs in the package threads or github issues, i will fix them ASAP.**
I have included a CHANGELOG.md with the download for those interested. ## Licenses -Code: Zander (content db: @zanderdev) GPL-3.0
-Textures: Zander (content db: @zanderdev) CC BY-SA 4.0
-See LICENSE.txt (for code) and LICENSE-TEXTURES.txt for more information. -IMPORTANT: Part of the code may be licensed diffrently. Make sure to check if there are any other notices. -This Game uses part of the code of Mesecons, Minetest Game, and potentially others. Their licenses can be found inside the repository. +Code: Zander and contributors GPL-3.0
+Textures: Zander and contributors CC BY-SA 4.0
+See LICENSE.txt (for code) and LICENSE-TEXTURES.txt for more information.
+ +Also: Parts of the code/other mods may be licensed differently. Make sure to check if there are any other notices. ## Soundtrack & SFX #### Main Menu @@ -30,6 +30,7 @@ Art by StumpyStrust, CC0, https://opengameart.org/content/space-skyboxes-0 - A Choice With Many Regrets by Tsorthan Grove, CC-BY 4.0, https://opengameart.org/content/a-choice-with-many-regrets #### SFX + - hard_drive_shut_down_01.wav by Noisehag -- https://freesound.org/s/110552/ -- License: Creative Commons 0 - [SFX Hit] deep kick boom by waveplaySFX -- https://freesound.org/s/231349/ -- License: Attribution 4.0 - 5.wav by steveygos93 -- https://freesound.org/s/103585/ -- License: Attribution 3.0 @@ -39,10 +40,10 @@ Art by StumpyStrust, CC0, https://opengameart.org/content/space-skyboxes-0 - MP5 Mag In (2) pt2.ogg by smill.and.welson -- https://freesound.org/s/698840/ -- License: Attribution 4.0 - pop.ogg by dodrio -- https://freesound.org/s/554022/ -- License: Creative Commons 0 - Page Turn by redagee -- https://freesound.org/s/737004/ -- License: Creative Commons 0 -- https://pixabay.com/sound-effects/rocket-loop-99748/ -- https://pixabay.com/sound-effects/distant-explosion-47562/ & https://pixabay.com/sound-effects/explosion-80108/ (edited) -- https://pixabay.com/sound-effects/door-lock-43124/ -- https://pixabay.com/sound-effects/050597-ice-crusher-38522/ (edited) +- Rocket Loop by Mozfoo -- https://freesound.org/s/458377/ -- License: Creative Commons 0 +- Explosion by morganpurkis -- https://freesound.org/s/397691/ -- License: Creative Commons 0 *AND* Distant Explosion by VanEngelen -- https://freesound.org/s/784660/ -- License: Creative Commons 0 (edited togheter) +- Door Lock by dster777 -- https://freesound.org/s/344551/ -- License: Creative Commons 0 +- Ice Crusher by karinalarasart -- https://freesound.org/s/440759/ -- License: Creative Commons 0 (edited) - https://freesound.org/people/dibko/sounds/619103/ -- License: Attribution 4.0 (edited) - https://freesound.org/people/newlocknew/sounds/536655/ -- License: Attribution 4.0 - https://freesound.org/people/MilanKovanda/sounds/624414/ -- License: Attribution 4.0 diff --git a/luanti_lsp_definitions b/luanti_lsp_definitions new file mode 160000 index 00000000..d2c647a0 --- /dev/null +++ b/luanti_lsp_definitions @@ -0,0 +1 @@ +Subproject commit d2c647a0c06295351dfba49846576c9967ec87a6 diff --git a/mods/biomegen/LICENSE b/mods/biomegen/LICENSE deleted file mode 100644 index 6600f1c9..00000000 --- a/mods/biomegen/LICENSE +++ /dev/null @@ -1,165 +0,0 @@ -GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/mods/biomegen/README.md b/mods/biomegen/README.md deleted file mode 100644 index 3b9ce182..00000000 --- a/mods/biomegen/README.md +++ /dev/null @@ -1,135 +0,0 @@ -# Biomegen - -Biome generator mod for Minetest, reproducing closely the biome generator provided by Minetest's core, but in Lua. Also includes an optional elevation adjustment parameter. - -It allows to use the biome systems on Lua mapgens (that do no allow to use core biome system). Since it reads registered biomes and decorations, it is compatible with all mods adding biomes/decos. - -Created by gaelysam (GaĆ«l de Sailly) in November 2020, licensed under LGPLv3.0. - -Version 2.0 (February 2024) - -# Include it in your mapgen - -`biomegen` should be triggered during mapgen function, after the loop, but before writing to the map. - -Your mapgen should generate only these 4 nodes: -- Stone (`mapgen_stone` / `default:stone`) -- Water (`mapgen_water_source` / `default:water_source`) -- River water (`mapgen_river_water_source` / `default:river_water_source`) -- Air (`air`) - -All other nodes will be ignored, no biome will be placed ontop of them. - -You should add `biomegen` as a dependancy of your mod (optional or mandatory). - -## API -Description of usual function parameters: -- `data`: Data containing the generated mapchunk -- `area`: VoxelArea helper object for data. `area = VoxelArea:new({MinEdge = emin, MaxEdge = emax})` -- `vm`: VoxelManip object -- `minp`: minimal coordinates of the chunk being generated, e.g. `{x=48, y=-32, z=208}` -- `maxp`: maximal coordinates of the chunk being generated, e.g. `{x=127, y=47, z=287}` -- `seed`: world-specific seed - -### `biomegen.generate_all(data, area, vm, minp, maxp, seed)` -All-in-one function to generate *biomes*, *decorations*, *ores* and *dust*. Includes a call to `vm:set_data` so no need to do it again. Using core function `minetest.generate_ores` for ores, so does not support biome-specific ores. - -### `biomegen.generate_biomes(data, area, minp, maxp)` -Generates biomes in `data`, according to biomes that have been registered using `minetest.register_biome`. - -### `biomegen.place_all_decos(data, area, vm, minp, maxp, seed)` -Generates decorations directly in `vm` (but reads `data` to know where to place them), according to decorations that have been registered using `minetest.register_decoration`. - -### `biomegen.dust_top_nodes(data, area, vm, minp, maxp)` -Drops 'dust' (usually snow) on biomes that require it. Like above, generates directly in `vm` but reads from `data`. If you used `place_all_decos` to generate decorations, you should update `data` from the `vm`: - -```lua -vm:get_data(data) -``` - -### `biomegen.gennotify(feature, pos)` -Adds an entry in gen notify in the field `feature` (`dungeon`, `cave_begin`, ..., also `decoration#id`). No effect if the feature is not requested in `minetest.set_gen_notify`. - -### `biomegen.set_elevation_chill(ec)` -Sets elevation chill coefficient. `0` means temperature does not depend on elevation (behaviour of core's biomegen). Usual values `0`-`0.5`. - -### `biomegen.skip_chunk(minp, maxp)` -Does not generate biomes but updates mapgen objects (`biomemap`, `heatmap`, `humiditymap`, `heightmap` and `gennotify`) so that other mods can use them without crashing. Use this function in the mapgen loop when skipping an empty chunk. - -### `minetest.get_mapgen_object(objname)` -The following objects are updated to take into account what Biomegen generates: -- `biomemap` -- `heatmap` -- `humiditymap` -- `heightmap` -- `gennotify` - -The behaviour of the function is otherwise unchanged. - -### `minetest.get_biome_data(pos) -Takes into account elevation if elevation chill coefficient is non-zero. The behaviour of the function is otherwise unchanged. - -## Examples -### Using `biomegen.generate_all` -```lua -local data = {} - -minetest.register_on_generated(function(minp, maxp, seed) - local vm, emin, emax = minetest.get_mapgen_object("voxelmanip") - local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax} - vm:get_data(data) - - ------------------------ - -- [MAPGEN LOOP HERE] -- - ------------------------ - - -- Generate biomes, decorations, ores and dust - biomegen.generate_all(data, area, vm, minp, maxp, seed) - - -- Calculate lighting for what has been created. - vm:calc_lighting() - -- Write what has been created to the world. - vm:write_to_map() - -- Liquid nodes were placed so set them flowing. - vm:update_liquids() -end) -``` - -### Equivalent with all functions -```lua -local data = {} - -minetest.register_on_generated(function(minp, maxp, seed) - local vm, emin, emax = minetest.get_mapgen_object("voxelmanip") - local area = VoxelArea:new{MinEdge = emin, MaxEdge = emax} - vm:get_data(data) - - ------------------------ - -- [MAPGEN LOOP HERE] -- - ------------------------ - - -- Generate biomes in 'data', using biomegen mod - biomegen.generate_biomes(data, area, minp, maxp) - - -- Write content ID data back to the voxelmanip. - vm:set_data(data) - -- Generate ores using core's function - minetest.generate_ores(vm, minp, maxp) - -- Generate decorations in VM (needs 'data' for reading) - biomegen.place_all_decos(data, area, vm, minp, maxp, seed) - -- Update data array to have ores/decorations - vm:get_data(data) - -- Add biome dust in VM (needs 'data' for reading) - biomegen.dust_top_nodes(data, area, vm, minp, maxp) - - -- Calculate lighting for what has been created. - vm:calc_lighting() - -- Write what has been created to the world. - vm:write_to_map() - -- Liquid nodes were placed so set them flowing. - vm:update_liquids() -end) -``` - -### Mapgen example -I have made a [modified version of `lvm_example`](https://github.com/Gael-de-Sailly/lvm_example/tree/biomegen) (mod originally by Paramat) to provide a minimal working example of a mapgen using `biomegen`. Try it! diff --git a/mods/biomegen/biomelist.lua b/mods/biomegen/biomelist.lua deleted file mode 100644 index eac26d8e..00000000 --- a/mods/biomegen/biomelist.lua +++ /dev/null @@ -1,72 +0,0 @@ --- biomelist.lua - -local core_cid = minetest.get_content_id -local function cid(name) - if not name then - return - end - local result - pcall(function() --< try - result = core_cid(name) - end) - if not result then - print("[biomegen] Node " .. name .. " not found!") - end - return result -end - -local function make_biomelist() - local biomes = {} - - for _, a in pairs(minetest.registered_biomes) do - local b = {} - b.name = a.name - biomes[b.name] = b - b.id = minetest.get_biome_id(a.name) - - if a.node_dust then - b.node_dust_name = a.node_dust - b.node_dust = cid(a.node_dust) - end - - b.node_top = cid(a.node_top) or cid("mapgen_stone") - b.depth_top = a.depth_top or 0 - - b.node_filler = cid(a.node_filler) or cid("mapgen_stone") - b.depth_filler = a.depth_filler or 0 - - b.node_stone = cid(a.node_stone) or cid("mapgen_stone") - - b.node_water_top = cid(a.node_water_top) or cid("mapgen_water_source") - b.depth_water_top = a.depth_water_top or 0 - - b.node_water = cid(a.node_water) or cid("mapgen_water_source") - b.node_river_water = cid(a.node_river_water) or cid("mapgen_river_water_source") - - b.node_riverbed = cid(a.node_riverbed) or cid("mapgen_stone") - b.depth_riverbed = a.depth_riverbed or 0 - - -- b.node_cave_liquid = ... - -- b.node_dungeon = ... - -- b.node_dungeon_alt = ... - -- b.node_dungeon_stair = ... - - b.min_pos = a.min_pos or {x=-31000, y=-31000, z=-31000} - if a.y_min then - b.min_pos.y = math.max(b.min_pos.y, a.y_min) - end - b.max_pos = a.max_pos or {x=31000, y=31000, z=31000} - if a.y_max then - b.max_pos.y = math.min(b.max_pos.y, a.y_max) - end - - b.vertical_blend = a.vertical_blend or 0 - - b.heat_point = a.heat_point or 50 - b.humidity_point = a.humidity_point or 50 - end - - return biomes -end - -return make_biomelist diff --git a/mods/biomegen/decorations.lua b/mods/biomegen/decorations.lua deleted file mode 100644 index 3d0ccc0a..00000000 --- a/mods/biomegen/decorations.lua +++ /dev/null @@ -1,245 +0,0 @@ --- decorations.lua - -local emptynodes = { - air = true, - ignore = true, -} - -local core_cid = minetest.get_content_id -local function cid(name) - if not name then - return - end - local result - pcall(function() --< try - result = core_cid(name) - end) - if not result then - print("[biomegen] Node " .. name .. " not found!") - end - return result -end - -local function generate_deco_simple(deco, vm, pr, p, ceiling) - local emin, emax = vm:get_emerged_area() - - local place_offset_y = deco.place_offset_y - if ceiling then - if p.y - place_offset_y - deco.height_max < emin.y then - return 0 - elseif p.y - 1 - place_offset_y > emax.y then - return 0 - end - else - if p.y + place_offset_y + deco.height_max > emax.y then - return 0 - elseif p.y + 1 + place_offset_y < emin.y then - return 0 - end - end - - local decos = deco.decoration - if #decos == 0 then - return 0 - end - local nodename = decos[pr:next(1, #decos)] - local height = deco.vary_height and pr:next(deco.height, deco.height_max) or deco.height - local param2 = deco.vary_param2 and pr:next(deco.param2, deco.param2_max) or deco.param2 - local force_placement = deco.flags.force_placement == true - - local direction = ceiling and -1 or 1 - p = { x = p.x, y = p.y + place_offset_y * direction, z = p.z } -- Deep-copy the table to avoid issues - for i = 1, height do - p.y = p.y + direction - local node = vm:get_node_at(p) - if not force_placement and not emptynodes[node.name] then - break - end - node.name = nodename - node.param2 = param2 - vm:set_node_at(p, node) - end - - return 1 -end - -local function get_schematic_size(schem) - if type(schem) == "table" then - return schem.size - elseif type(schem) == "string" then - local mts = io.open(schem) - if not mts then - return { x = 0, y = 0, z = 0 } - end - mts:seek('set', 6) - local sx1, sx2, sy1, sy2, sz1, sz2 = mts:read(6):byte() - mts:close() - return { x = sx1 * 256 + sx2, y = sy1 * 256 + sy2, z = sz1 * 256 + sz2 } - end - - return { x = 0, y = 0, z = 0 } -end - -local function generate_deco_schematic(deco, vm, pr, p, ceiling) - local force_placement = deco.flags.force_placement == true - local direction = ceiling and -1 or 1 - if not deco.flags.place_center_y then - if ceiling then - local size = get_schematic_size(schem) - p.y = p.y - deco.place_offset_y - size.y + 1 - else - p.y = p.y + deco.place_offset_y - end - end - - minetest.place_schematic_on_vmanip(vm, p, deco.schematic, deco.rotation, deco.replacements, force_placement, - deco.schem_flags) - - return 1 -end - -local function parse_node_list(raw_list) - if not raw_list then - return {} - end - local ilist = {} - if type(raw_list) == "string" then - raw_list = { raw_list } - end - - for i, node in ipairs(raw_list) do - if node:sub(1, 6) == "group:" then - local groupname = node:sub(7, -1) - for name, ndef in pairs(minetest.registered_nodes) do - if ndef.groups and ndef.groups[groupname] and ndef.groups[groupname] > 0 then - local id = cid(name) - if id then - ilist[id] = true - end - end - end - else - local id = cid(node) - if id then - ilist[id] = true - end - end - end - - return ilist -end - -local function make_decolist(gennotify_decolist) - local decos = {} - - local gennotify_decos = {} - for _, v in ipairs(gennotify_decolist) do - gennotify_decos[v] = true - end - - for i, a in pairs(minetest.registered_decorations) do - local b = {} - decos[i] = b - - local id = minetest.get_decoration_id(i) - if id and gennotify_decos[id] then - b.gennotify = "decoration#" .. id - end - - b.name = a.name or "unnamed " .. i - - b.deco_type = a.deco_type or "simple" - - b.place_on = parse_node_list(a.place_on) - - b.sidelen = a.sidelen or 8 - b.fill_ratio = a.fill_ratio or 0.02 - local np = a.noise_params - b.use_noise = false - if np then - b.use_noise = true - b.noise = minetest.get_perlin(np) - end - - b.use_biomes = false - if a.biomes then - local biomes_raw = a.biomes - b.use_biomes = true - if type(biomes_raw) == "table" then - local biomes = {} - b.biomes = biomes - for i, biome in pairs(biomes_raw) do - if type(biome) == "number" then - biome = minetest.get_biome_name(biome) - end - biomes[biome] = true - end - else - if type(biomes_raw) == "number" then - biomes_raw = minetest.get_biome_name(biomes_raw) - end - b.biomes = { [biomes_raw] = true } - end - end - - b.y_min = a.y_min or -31000 - b.y_max = a.y_max or 31000 - - b.spawn_by = parse_node_list(a.spawn_by) - b.num_spawn_by = a.num_spawn_by or 0 - - local flags_raw = a.flags or "" - local flags = {} - b.flags = flags - for i, flag in ipairs(flags_raw:split()) do - flag = flag:trim() - local status = true - if flag:sub(1, 2) == "no" then - flag = flag:sub(3, -1) - status = false - end - flags[flag] = status - end - - if b.deco_type == "simple" then - local a_deco = a.decoration - if type(a_deco) == "string" then - a_deco = { a_deco } - end - local b_deco = {} - for _, deco in ipairs(a_deco) do - if cid(deco) then - table.insert(b_deco, deco) - end - end - b.decoration = b_deco - b.height = a.height or 1 - b.height_max = math.max(a.height_max or b.height, b.height) - b.vary_height = b.height < b.height_max - b.param2 = a.param2 or 0 - b.param2_max = math.max(a.params2_max or b.param2, b.param2) - b.vary_param2 = b.param2 < b.param2_max - b.place_offset_y = a.place_offset_y or 0 - b.generate = generate_deco_simple - elseif b.deco_type == "schematic" then - b.schematic = a.schematic - b.replacements = a.replacements or {} - b.rotation = a.rotation or 0 - b.place_offset_y = a.place_offset_y or 0 - - local schem_flags = {} - for _, flag in ipairs({ 'place_center_x', 'place_center_y', 'place_center_z' }) do - if flags[flag] then - table.insert(schem_flags, flag) - end - end - b.schem_flags = table.concat(schem_flags, ',') - - b.generate = generate_deco_schematic - end - end - - return decos -end - -return make_decolist diff --git a/mods/biomegen/init.lua b/mods/biomegen/init.lua deleted file mode 100644 index 537d36c6..00000000 --- a/mods/biomegen/init.lua +++ /dev/null @@ -1,707 +0,0 @@ --- biomegen/init.lua - -local make_biomelist = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/biomelist.lua") -local make_decolist = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/decorations.lua") - -local np_filler_depth = { - offset = 0, - scale = 1.2, - spread = {x=150, y=150, z=150}, - seed = 261, - octaves = 3, - persist = 0.7, - lacunarity = 2.0, -} - -local nobj_filler_depth, nobj_heat, nobj_heat_blend, nobj_humid, nobj_humid_blend -local nvals_filler_depth = {} -local nvals_heat = {} -local nvals_heat_blend = {} -local nvals_humid = {} -local nvals_humid_blend = {} - -local water_level = tonumber(minetest.get_mapgen_setting('water_level')) -local elevation_chill = 0 -local function set_elevation_chill(ec) - elevation_chill = ec -end - -local init_mapgen = false -local init_biomes = false - -local c_ignore -local c_air -local c_stone -local c_water -local c_rwater - -local biomes, decos - -local gennotify_flags = {} - -local function initialize_biome_data() - print("[biomegen] Initializing") - - init_biomes = true - - local gennotify_flagstr, gennotify_decolist = minetest.get_gen_notify() - local notify_decos = false - for _, v in ipairs(gennotify_flagstr:split(',')) do - v = v:trim() - if v == "decoration" then - notify_decos = true - else - gennotify_flags[v] = true - end - end - - if notify_decos then - for _, v in ipairs(gennotify_decolist) do - gennotify_flags["decoration#" .. v] = true - end - end - - biomes = make_biomelist() - decos = make_decolist(notify_decos and gennotify_decolist or {}) -end - -local function initialize_mapgen_data(chulens) - init_mapgen = true - - if not init_biomes then - initialize_biome_data() - end - - local noiseparams = minetest.get_mapgen_setting_noiseparams - - local chulens2d = {x=chulens.x, y=chulens.z, z=1} - local np_heat = noiseparams('mg_biome_np_heat') - np_heat.offset = np_heat.offset + water_level*elevation_chill - nobj_filler_depth = minetest.get_perlin_map(np_filler_depth, chulens2d) - nobj_heat = minetest.get_perlin_map(np_heat, chulens2d) - nobj_heat_blend = minetest.get_perlin_map(noiseparams('mg_biome_np_heat_blend'), chulens2d) - nobj_humid = minetest.get_perlin_map(noiseparams('mg_biome_np_humidity'), chulens2d) - nobj_humid_blend = minetest.get_perlin_map(noiseparams('mg_biome_np_humidity_blend'), chulens2d) - - c_ignore = minetest.get_content_id("ignore") - c_air = minetest.get_content_id("air") - c_stone = minetest.get_content_id("mapgen_stone") - c_water = minetest.get_content_id("mapgen_water_source") - c_rwater = minetest.get_content_id("mapgen_river_water_source") -end - -local biomemap = {} -local heatmap = {} -local humidmap = {} -local heightmap = {} -local gennotify = {} -local imax = 0 - -local function add_gennotify(feature, pos) - if not init_biomes then - initialize_biome_data() - end - - if not gennotify_flags[feature] then - return - end - gennotify[feature] = gennotify[feature] or {} - table.insert(gennotify[feature], pos) -end - -local function calculate_noises(minp) - local minp2d = {x=minp.x, y=minp.z} - nobj_filler_depth:get_2d_map_flat(minp2d, nvals_filler_depth) - - nobj_heat:get_2d_map_flat(minp2d, nvals_heat) - nobj_heat_blend:get_2d_map_flat(minp2d, nvals_heat_blend) - - nobj_humid:get_2d_map_flat(minp2d, nvals_humid) - nobj_humid_blend:get_2d_map_flat(minp2d, nvals_humid_blend) - - for i, heat in ipairs(nvals_heat) do -- use nvals_heat to iterate, could have been another one - heatmap[i] = heat + nvals_heat_blend[i] - humidmap[i] = nvals_humid[i] + nvals_humid_blend[i] - end -end - -local function calc_biome_from_noise(heat, humid, pos) - local biome_closest = nil - local biome_closest_blend = nil - local dist_min = 31000 - local dist_min_blend = 31000 - - for i, biome in pairs(biomes) do - local min_pos, max_pos = biome.min_pos, biome.max_pos - if pos.y >= min_pos.y and pos.y <= max_pos.y+biome.vertical_blend - and pos.x >= min_pos.x and pos.x <= max_pos.x - and pos.z >= min_pos.z and pos.z <= max_pos.z then - local d_heat = heat - biome.heat_point - local d_humid = humid - biome.humidity_point - local dist = d_heat*d_heat + d_humid*d_humid -- Pythagorean distance - - if pos.y <= max_pos.y then -- Within y limits of biome - if dist < dist_min then - dist_min = dist - biome_closest = biome - end - elseif dist < dist_min_blend then -- Blend area above biome - dist_min_blend = dist - biome_closest_blend = biome - end - end - end - - -- Carefully tune pseudorandom seed variation to avoid single node dither - -- and create larger scale blending patterns similar to horizontal biome - -- blend. - local seed = math.floor(pos.y + (heat+humid) * 0.9) - local rng = PseudoRandom(seed) - - if biome_closest_blend and dist_min_blend <= dist_min - and rng:next(0, biome_closest_blend.vertical_blend) >= pos.y - biome_closest_blend.max_pos.y then - return biome_closest_blend - end - - return biome_closest -end - -local function get_biome_at_index(i, pos) - local heat = heatmap[i] - math.max(pos.y, water_level)*elevation_chill - local humid = humidmap[i] - return calc_biome_from_noise(heat, humid, pos) -end - --- Walkable, liquid, and dustable: memoization tables for better performance -local walkable = setmetatable({}, { - __index = function(t, c) - local is_walkable = false - local ndef = minetest.registered_nodes[minetest.get_name_from_content_id(c)] - if ndef and ndef.walkable then - is_walkable = true - end - - t[c] = is_walkable - return is_walkable - end, -}) - -local liquid = setmetatable({}, { - __index = function(t, c) - local is_liquid = false - local ndef = minetest.registered_nodes[minetest.get_name_from_content_id(c)] - if ndef and ndef.liquidtype then - is_liquid = ndef.liquidtype ~= "none" - end - - t[c] = is_liquid - return is_liquid - end, -}) - -local dustable = setmetatable({}, { - __index = function(t, c) - local is_dustable = false - local ndef = minetest.registered_nodes[minetest.get_name_from_content_id(c)] - if ndef and ndef.walkable then - local dtype = ndef.drawtype - if dtype and dtype == "normal" or dtype == "allfaces" or dtype == "allfaces_optional" or dtype == "glasslike" or dtype == "glasslike_framed" or dtype == "glasslike_framed_optional" then - is_dustable = true - end - end - - t[c] = is_dustable - return is_dustable - end, -}) - -local function generate_biomes(data, a, minp, maxp) - if not init_mapgen then - local chulens = {x=maxp.x-minp.x+1, y=maxp.y-minp.y+1, z=maxp.z-minp.z+1} - initialize_mapgen_data(chulens) - end - - calculate_noises(minp) - - local index = 1 - for z=minp.z, maxp.z do - for x=minp.x, maxp.x do - local biome = nil - local water_biome = nil - local biome_stone = c_stone - - local depth_top = 0 - local base_filler = 0 - local depth_water_top = 0 - local depth_riverbed = 0 - - local biome_y_min = -31000 - local y_start = maxp.y - local vi = a:index(x, maxp.y, z) - local ystride = a.ystride - - local c_above = data[vi+ystride] - if c_above == c_ignore then - y_start = y_start - 1 - c_above = data[vi] - vi = vi - ystride - end - local air_above = c_above == c_air - local river_water_above = c_above == c_rwater - local water_above = c_above == c_water or river_water_above - - biomemap[index] = nil - heightmap[index] = -31000 - - local nplaced = (air_above or water_above) and 0 or 31000 - - for y=y_start, minp.y-1, -1 do - local c = data[vi] - if heightmap[index] == -31000 and walkable[c] then - heightmap[index] = y - end - - local is_stone_surface = (c == c_stone) and - (air_above or water_above or not biome or y < biome_y_min) - local is_water_surface = (c == c_water or c == c_rwater) and - (air_above or not biome or y < biome_y_min) - - if is_stone_surface or is_water_surface then - biome = get_biome_at_index(index, {x=x, y=y, z=z}) - biome_stone = biome.node_stone - - if not biomemap[index] and is_stone_surface then - biomemap[index] = biome - end - - if not water_biome and is_water_surface then - water_biome = biome - end - - depth_top = biome.depth_top - base_filler = math.max(depth_top + biome.depth_filler + nvals_filler_depth[index], 0) - depth_water_top = biome.depth_water_top - depth_riverbed = biome.depth_riverbed - biome_y_min = biome.min_pos.y - end - - if c == c_stone or c == biome_stone then - local c_below = data[vi-ystride] - if c_below == c_air or c_below == c_rwater or c_below == c_water then - nplaced = 31000 - end - if river_water_above then - if nplaced < depth_riverbed then - data[vi] = biome.node_riverbed - nplaced = nplaced + 1 - else - nplaced = 31000 - river_water_above = false - end - elseif nplaced < depth_top then - data[vi] = biome.node_top - nplaced = nplaced + 1 - elseif nplaced < base_filler then - data[vi] = biome.node_filler - nplaced = nplaced + 1 - else - data[vi] = biome_stone - nplaced = 31000 - end - - air_above = false - water_above = false - elseif c == c_water then - if y > water_level-depth_water_top then - data[vi] = biome.node_water_top - else - data[vi] = biome.node_water - end - nplaced = 0 - air_above = false - water_above = true - elseif c == c_rwater then - data[vi] = biome.node_river_water - nplaced = 0 - air_above = false - water_above = true - river_water_above = true - elseif c == c_air then - nplaced = 0 - air_above = true - water_above = false - else - nplaced = 31000 - air_above = false - water_above = false - end - - vi = vi - ystride - end - - if not biomemap[index] then - biomemap[index] = water_biome - end - - index = index + 1 - end - end - - imax = index -end - -local function skip_chunk(minp, maxp) - if not init_mapgen then - local chulens = {x=maxp.x-minp.x+1, y=maxp.y-minp.y+1, z=maxp.z-minp.z+1} - initialize_mapgen_data(chulens) - end - - calculate_noises(minp) - - local index = 1 - for z=minp.z, maxp.z do - for x=minp.x, maxp.x do - biomemap[index] = nil - heightmap[index] = -31000 - index = index + 1 - end - end - imax = index -end - -local function can_place_deco(deco, data, vi, pattern) - if not deco.place_on[data[vi]] then - return false - elseif deco.num_spawn_by <= 0 then - return true - end - - local spawn_by = deco.spawn_by - local nneighs = deco.num_spawn_by - for i, incr in ipairs(pattern) do - vi = vi + incr - if spawn_by[data[vi]] then - nneighs = nneighs - 1 - if nneighs < 1 then - return true - end - end - end - - return false -end - -local function place_deco(deco, data, a, vm, minp, maxp, blockseed) - local ps = PcgRandom(blockseed + 53) - local carea_size = maxp.x - minp.x + 1 - - local sidelen = deco.sidelen - if carea_size % sidelen > 0 then - sidelen = carea_size - end - local divlen = carea_size / sidelen - 1 - local area = sidelen*sidelen - local ystride, zstride = a.ystride, a.zstride - local pattern = {1, zstride, -1, -1, -zstride, -zstride, 1, 1, ystride, zstride, zstride, -1, -1, -zstride, -zstride, 1} -- Successive increments to iterate over 16 neighbouring nodes - - local gennotify_list = {} - if deco.gennotify then - gennotify[deco.gennotify] = nil - end - - for z0=0, divlen do - for x0=0, divlen do - local p2d_center = {x=minp.x+sidelen*(x0+0.5), y=minp.z+sidelen*(z0+0.5)} - local p2d_min = {x=minp.x+sidelen*x0, y=minp.z+sidelen*z0} - local p2d_max = {x=minp.x+sidelen*(x0+1)-1, y=minp.z+sidelen*(z0+1)-1} - - local cover = false - local nval = deco.use_noise and deco.noise:get_2d(p2d_center) or deco.fill_ratio - local deco_count = 0 - - if nval >= 10 then - cover = true - deco_count = area - else - local deco_count_f = area * nval - if deco_count_f >= 1 then - deco_count = deco_count_f - elseif deco_count_f > 0 and ps:next(1, 1000) <= deco_count_f * 1000 then - deco_count = 1 - end - end - - local x = p2d_min.x - 1 - local z = p2d_min.y - - for i=1, deco_count do - if not cover then - x = ps:next(p2d_min.x, p2d_max.x) - z = ps:next(p2d_min.y, p2d_max.y) - else - x = x + 1 - if x == p2d_max.x + 1 then - z = z + 1 - x = p2d_min.x - end - end - local mapindex = carea_size * (z - minp.z) + (x - minp.x) + 1 - - if deco.flags.all_floors or deco.flags.all_ceilings then - local biome_ok = true - if deco.use_biomes then - local biome_here = biomemap[mapindex] - if biome_here and not deco.biomes[biome_here.name] then - biome_ok = false - end - end - - if biome_ok then - local size = (maxp.x - minp.x + 1) / 2 - local floors = {} - local ceilings = {} - - local is_walkable = false - local vi = a:index(x, maxp.y, z) - local walkable_above = walkable[data[vi]] - for y = maxp.y-1, minp.y, -1 do - vi = vi - ystride - is_walkable = walkable[data[vi]] - if is_walkable and not walkable_above then - table.insert(floors, y) - elseif walkable_above and not is_walkable then - table.insert(ceilings, y+1) - end - - walkable_above = is_walkable - end - - if deco.flags.all_floors then - for _, y in ipairs(floors) do - if y >= deco.y_min and y <= deco.y_max - and can_place_deco(deco, data, a:index(x,y,z), pattern) then - local pos = {x=x, y=y, z=z} - local gen = deco:generate(vm, ps, pos, false) - if gen > 0 and deco.gennotify then - gennotify_list[#gennotify_list+1] = pos - end - end - end - end - - if deco.flags.all_ceilings then - for _, y in ipairs(ceilings) do - if y >= deco.y_min and y <= deco.y_max - and can_place_deco(deco, data, a:index(x,y,z), pattern) then - local pos = {x=x, y=y, z=z} - local gen = deco:generate(vm, ps, pos, true) - if gen > 0 and deco.gennotify then - gennotify_list[#gennotify_list+1] = pos - end - end - end - end - end - else - local y = -31000 - if deco.flags.liquid_surface then - local vi = a:index(x, maxp.y, z) - for yi=maxp.y, minp.y, -1 do - local c = data[vi] - if walkable[c] then - break - elseif liquid[c] then - y = yi - break - end - vi = vi - ystride - end - else - local vi = a:index(x, maxp.y, z) - for yi=maxp.y, minp.y, -1 do - if walkable[data[vi]] then - y = yi - break - end - vi = vi - ystride - end - end - - if y >= deco.y_min and y <= deco.y_max and y >= minp.y and y <= maxp.y then - local biome_ok = true - if deco.use_biomes then - local biome_here = biomemap[mapindex] - if biome_here and not deco.biomes[biome_here.name] then - biome_ok = false - end - end - - if biome_ok then - local pos = {x=x, y=y, z=z} - if can_place_deco(deco, data, a:index(x,y,z), pattern) then - local gen = deco:generate(vm, ps, pos, false) - if gen > 0 and deco.gennotify then - gennotify_list[#gennotify_list+1] = pos - end - end - end - end - end - end - end - end - - if #gennotify_list > 0 then - gennotify[deco.gennotify] = gennotify_list - end - - return 0 -end - -local function get_blockseed(p, seed) - return seed + p.z * 38134234 + p.y * 42123 + p.x * 23 -end - -local function place_all_decos(data, a, vm, minp, maxp, seed) - local emin = vm:get_emerged_area() - local blockseed = get_blockseed(emin, seed) - - local nplaced = 0 - - for i, deco in pairs(decos) do - nplaced = nplaced + place_deco(deco, data, a, vm, minp, maxp, blockseed) - end - - return nplaced -end - -local function dust_top_nodes(data, a, vm, minp, maxp) - if maxp.y < water_level then - return - end - - local full_maxp = a.MaxEdge - - local index = 1 - local ystride = a.ystride - - for z = minp.z, maxp.z do - for x = minp.x, maxp.x do - local biome = biomemap[index] - - if biome and biome.node_dust then - local vi = a:index(x, full_maxp.y, z) - local c_full_max = data[vi] - local y_start - - if c_full_max == c_air then - y_start = full_maxp.y - 1 - elseif c_full_max == c_ignore then - vi = a:index(x, maxp.y, z) - local c_max = data[vi] - - if c_max == c_air then - y_start = maxp.y - end - end - - if y_start then -- workaround for the 'continue' statement - vi = a:index(x, y_start, z) - local y = y_start - for y0=y_start, minp.y-1, -1 do - if data[vi] ~= c_air then - y = y0 - break - end - vi = vi - ystride - end - local c = data[vi] - if dustable[c] and c ~= biome.node_dust then - local pos = {x=x, y=y+1, z=z} - vm:set_node_at(pos, {name=biome.node_dust_name}) - end - end - end - index = index + 1 - end - end -end - -local orig_get_mapgen_object = minetest.get_mapgen_object -function minetest.get_mapgen_object(objname) - if objname == "biomemap" then - local bmap = {} - for i=1, imax do - bmap[i] = biomemap[i] and biomemap[i].id or 0 - end - return bmap - end - - if objname == "heatmap" then - return table.copy(heatmap) - end - - if objname == "humiditymap" then - return table.copy(humidmap) - end - - if objname == "heightmap" then - return table.copy(heightmap) - end - - if objname == "gennotify" then - return table.copy(gennotify) - end - - return orig_get_mapgen_object(objname) -end - -local orig_get_biome_data = minetest.get_biome_data -function minetest.get_biome_data(pos) - if not init_biomes then - initialize_biome_data() - end - - if elevation_chill == 0 then - return orig_get_biome_data(pos) - end - - local heat = minetest.get_heat(pos) + math.max(water_level - pos.y, 0)*elevation_chill - local humidity = minetest.get_humidity(pos) - local biome = calc_biome_from_noise(heat, humidity, pos) - if biome then - return { - heat = heat, - humidity = humidity, - biome = biome.id, - } - end -end - --- Reset gennotify after mapgen (ensure it is called last) -minetest.register_on_mods_loaded(function() - minetest.register_on_generated(function(minp, maxp, seed) - gennotify = {} - end) -end) - -biomegen = { - set_elevation_chill = set_elevation_chill, - calculate_noises = calculate_noises, - get_biome_at_index = get_biome_at_index, - calc_biome_from_noise = calc_biome_from_noise, - generate_biomes = generate_biomes, - place_all_decos = place_all_decos, - dust_top_nodes = dust_top_nodes, - skip_chunk = skip_chunk, - gennotify = add_gennotify, -} - -function biomegen.generate_all(data, a, vm, minp, maxp, seed) - generate_biomes(data, a, minp, maxp) - vm:set_data(data) - place_all_decos(data, a, vm, minp, maxp, seed) - minetest.generate_ores(vm, minp, maxp) - vm:get_data(data) - dust_top_nodes(data, a, vm, minp, maxp) -end diff --git a/mods/biomegen/mod.conf b/mods/biomegen/mod.conf deleted file mode 100644 index 4b4f57db..00000000 --- a/mods/biomegen/mod.conf +++ /dev/null @@ -1,3 +0,0 @@ -name = biomegen -author = gaelysam -title = BiomeGen diff --git a/mods/controls b/mods/controls new file mode 160000 index 00000000..61376a36 --- /dev/null +++ b/mods/controls @@ -0,0 +1 @@ +Subproject commit 61376a365dbada466c8f5dbca0613d9463f7b276 diff --git a/mods/controls/.github/workflows/luacheck.yml b/mods/controls/.github/workflows/luacheck.yml deleted file mode 100644 index b5016503..00000000 --- a/mods/controls/.github/workflows/luacheck.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: luacheck -on: [push, pull_request] -jobs: - luacheck: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@master - - name: Luacheck - uses: lunarmodules/luacheck@master \ No newline at end of file diff --git a/mods/controls/.luacheckrc b/mods/controls/.luacheckrc deleted file mode 100644 index a68b4397..00000000 --- a/mods/controls/.luacheckrc +++ /dev/null @@ -1,8 +0,0 @@ - -globals = { - "controls", -} - -read_globals = { - "minetest", -} diff --git a/mods/controls/debug.lua b/mods/controls/debug.lua deleted file mode 100644 index f9f91cda..00000000 --- a/mods/controls/debug.lua +++ /dev/null @@ -1,21 +0,0 @@ -controls.register_on_press(function(player, key) - local name = player:get_player_name() - minetest.chat_send_player(name, name .. " pressed " .. key) -end) - -controls.register_on_hold(function(player, key, length) - local name = player:get_player_name() - minetest.chat_send_player(name, name .. " held " .. key .. " for " .. length .. " seconds") -end) - -controls.register_on_release(function(player, key, length) - local name = player:get_player_name() - minetest.chat_send_player(name, name .. " released " .. key .. " after " .. length .. " seconds") -end) - -minetest.register_on_joinplayer(function(player) - local name = player:get_player_name() - minetest.chat_send_player(name, #controls.registered_on_press .. " registered_on_press callbacks") - minetest.chat_send_player(name, #controls.registered_on_hold .. " registered_on_hold callbacks") - minetest.chat_send_player(name, #controls.registered_on_release .. " registered_on_release callbacks") -end) diff --git a/mods/controls/init.lua b/mods/controls/init.lua deleted file mode 100644 index 53903a89..00000000 --- a/mods/controls/init.lua +++ /dev/null @@ -1,67 +0,0 @@ -controls = { - registered_on_press = {}, - registered_on_hold = {}, - registered_on_release = {}, - players = {}, -} - -function controls.register_on_press(callback) - table.insert(controls.registered_on_press, callback) -end - -function controls.register_on_hold(callback) - table.insert(controls.registered_on_hold, callback) -end - -function controls.register_on_release(callback) - table.insert(controls.registered_on_release, callback) -end - -minetest.register_on_joinplayer(function(player) - local name = player:get_player_name() - controls.players[name] = {} - for key in pairs(player:get_player_control()) do - controls.players[name][key] = {false} - end -end) - -minetest.register_on_leaveplayer(function(player) - local name = player:get_player_name() - controls.players[name] = nil -end) - -local function update_player_controls(player, player_controls) - local time_now = minetest.get_us_time() - for key, pressed in pairs(player:get_player_control()) do - if player_controls[key] then - if pressed and not player_controls[key][1] then - for _, callback in pairs(controls.registered_on_press) do - callback(player, key) - end - player_controls[key] = {true, time_now} - elseif pressed and player_controls[key][1] then - for _, callback in pairs(controls.registered_on_hold) do - callback(player, key, (time_now - player_controls[key][2]) / 1e6) - end - elseif not pressed and player_controls[key][1] then - for _, callback in pairs(controls.registered_on_release) do - callback(player, key, (time_now - player_controls[key][2]) / 1e6) - end - player_controls[key] = {false} - end - end - end -end - -minetest.register_globalstep(function() - for _, player in pairs(minetest.get_connected_players()) do - local name = player:get_player_name() - if controls.players[name] then - update_player_controls(player, controls.players[name]) - end - end -end) - -if minetest.settings:get_bool("controls_enable_debug", false) then - dofile(minetest.get_modpath("controls") .. "/debug.lua") -end diff --git a/mods/controls/license b/mods/controls/license deleted file mode 100644 index 0f696e49..00000000 --- a/mods/controls/license +++ /dev/null @@ -1,24 +0,0 @@ -License of source code ----------------------- - -The MIT License (MIT) - -Copyright (c) 2023 wsor4035(aka wsor) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/mods/controls/mod.conf b/mods/controls/mod.conf deleted file mode 100644 index 2736ee19..00000000 --- a/mods/controls/mod.conf +++ /dev/null @@ -1,3 +0,0 @@ -name = controls -description = Utility library for control press/hold/release events -min_minetest_version = 5.0.0 diff --git a/mods/controls/readme.md b/mods/controls/readme.md deleted file mode 100644 index 6ef607e8..00000000 --- a/mods/controls/readme.md +++ /dev/null @@ -1,35 +0,0 @@ -# Controls [controls] - -[![luacheck](https://github.com/mt-mods/controls/workflows/luacheck/badge.svg)](https://github.com/mt-mods/controls/actions) -[![ContentDB](https://content.minetest.net/packages/mt-mods/controls/shields/downloads/)](https://content.minetest.net/packages/mt-mods/controls/) - -Utility library for control press/hold/release events. - -Rewritten and maintained version of [Arcelmi/minetest-controls](https://github.com/Arcelmi/minetest-controls). - - -## API - -Callbacks are supported for all keys in `player:get_player_control()`. - -```lua -controls.register_on_press(function(player, key) - -- Called when a key is pressed - -- player: player object - -- key: key pressed -end) - -controls.register_on_hold(function(player, key, length) - -- Called every globalstep while a key is held - -- player: player object - -- key: key pressed - -- length: length of time key has been held in seconds -end) - -controls.register_on_release(function(player, key, length) - -- Called when a key is released - -- player: player object - -- key: key pressed - -- length: length of time key was held in seconds -end) -``` diff --git a/mods/controls/settingtypes.txt b/mods/controls/settingtypes.txt deleted file mode 100644 index 34075551..00000000 --- a/mods/controls/settingtypes.txt +++ /dev/null @@ -1,2 +0,0 @@ -# Enable debug mod for player controls. Sends chat messages to the player when callbacks are called. -controls_enable_debug (Enable debug mode) bool false diff --git a/mods/drawers/api.lua b/mods/drawers/api.lua index 3f9a932d..f1a749ac 100755 --- a/mods/drawers/api.lua +++ b/mods/drawers/api.lua @@ -25,430 +25,390 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]] -local S = minetest.get_translator('drawers') +local S = minetest.get_translator 'drawers' drawers.node_box_simple = { - { -0.5, -0.5, -0.4375, 0.5, 0.5, 0.5 }, - { -0.5, -0.5, -0.5, -0.4375, 0.5, -0.4375 }, - { 0.4375, -0.5, -0.5, 0.5, 0.5, -0.4375 }, - { -0.4375, 0.4375, -0.5, 0.4375, 0.5, -0.4375 }, - { -0.4375, -0.5, -0.5, 0.4375, -0.4375, -0.4375 }, + { -0.5, -0.5, -0.4375, 0.5, 0.5, 0.5 }, + { -0.5, -0.5, -0.5, -0.4375, 0.5, -0.4375 }, + { 0.4375, -0.5, -0.5, 0.5, 0.5, -0.4375 }, + { -0.4375, 0.4375, -0.5, 0.4375, 0.5, -0.4375 }, + { -0.4375, -0.5, -0.5, 0.4375, -0.4375, -0.4375 }, } -drawers.drawer_formspec = "size[9,6.7]" .. - "list[context;upgrades;2,0.5;5,1;]" .. - drawers.inventory_list(2.5) .. - "listring[context;upgrades]" .. - "listring[current_player;main]" .. - drawers.get_upgrade_slots_bg(2, 0.5) +drawers.drawer_formspec = 'size[9,6.7]' + .. 'list[context;upgrades;2,0.5;5,1;]' + .. drawers.inventory_list(2.5) + .. 'listring[context;upgrades]' + .. 'listring[current_player;main]' + .. drawers.get_upgrade_slots_bg(2, 0.5) -- construct drawer function drawers.drawer_on_construct(pos) - local node = core.get_node(pos) - local ndef = core.registered_nodes[node.name] - local drawerType = ndef.groups.drawer - - local base_stack_max = core.nodedef_default.stack_max or 99 - local stack_max_factor = ndef.drawer_stack_max_factor or 24 -- 3x8 - stack_max_factor = math.floor(stack_max_factor / drawerType) -- drawerType => number of drawers in node - - -- meta - local meta = core.get_meta(pos) - - local i = 1 - while i <= drawerType do - local vid = i - -- 1x1 drawers don't have numbers in the meta fields - if drawerType == 1 then vid = "" end - meta:set_string("name" .. vid, "") - meta:set_int("count" .. vid, 0) - meta:set_int("max_count" .. vid, base_stack_max * stack_max_factor) - meta:set_int("base_stack_max" .. vid, base_stack_max) - meta:set_string("entity_infotext" .. vid, drawers.gen_info_text(S("Empty"), 0, - stack_max_factor, base_stack_max)) - meta:set_int("stack_max_factor" .. vid, stack_max_factor) - - i = i + 1 - end - - -- spawn all visuals - drawers.spawn_visuals(pos) - - -- create drawer upgrade inventory - meta:get_inventory():set_size("upgrades", 5) - - -- set the formspec - meta:set_string("formspec", drawers.drawer_formspec) + local node = core.get_node(pos) + local ndef = core.registered_nodes[node.name] + local drawerType = ndef.groups.drawer + + local base_stack_max = core.nodedef_default.stack_max or 99 + local stack_max_factor = ndef.drawer_stack_max_factor or 24 -- 3x8 + stack_max_factor = math.floor(stack_max_factor / drawerType) -- drawerType => number of drawers in node + + -- meta + local meta = core.get_meta(pos) + + local i = 1 + while i <= drawerType do + local vid = i + -- 1x1 drawers don't have numbers in the meta fields + if drawerType == 1 then vid = '' end + meta:set_string('name' .. vid, '') + meta:set_int('count' .. vid, 0) + meta:set_int('max_count' .. vid, base_stack_max * stack_max_factor) + meta:set_int('base_stack_max' .. vid, base_stack_max) + meta:set_string('entity_infotext' .. vid, drawers.gen_info_text(S 'Empty', 0, stack_max_factor, base_stack_max)) + meta:set_int('stack_max_factor' .. vid, stack_max_factor) + + i = i + 1 + end + + -- spawn all visuals + drawers.spawn_visuals(pos) + + -- create drawer upgrade inventory + meta:get_inventory():set_size('upgrades', 5) + + -- set the formspec + meta:set_string('formspec', drawers.drawer_formspec) end -- destruct drawer function drawers.drawer_on_destruct(pos) - drawers.remove_visuals(pos) + drawers.remove_visuals(pos) - -- clean up visual cache - if drawers.drawer_visuals[core.hash_node_position(pos)] then - drawers.drawer_visuals[core.hash_node_position(pos)] = nil - end + -- clean up visual cache + if drawers.drawer_visuals[core.hash_node_position(pos)] then + drawers.drawer_visuals[core.hash_node_position(pos)] = nil + end end -- don't drop all items function drawers.drawer_on_dig(pos, node, player) - local drawerType = 1 - if core.registered_nodes[node.name] then - drawerType = core.registered_nodes[node.name].groups.drawer - end - if core.is_protected(pos, player:get_player_name()) then - core.record_protection_violation(pos, player:get_player_name()) - return false - end - - - local meta = core.get_meta(pos) - local k = 1 - while k <= drawerType do - -- don't add a number in meta fields for 1x1 drawers - local vid = tostring(k) - if drawerType == 1 then vid = "" end - local count = meta:get_int("count" .. vid) - k = k + 1 - if count > 0 then return end - end - - -- drop all drawer upgrades - local upgrades = meta:get_inventory():get_list("upgrades") - if upgrades then - for _, itemStack in pairs(upgrades) do - if itemStack:get_count() > 0 then - return false - end - end - end - -- remove node - core.node_dig(pos, node, player) + local drawerType = 1 + if core.registered_nodes[node.name] then drawerType = core.registered_nodes[node.name].groups.drawer end + if core.is_protected(pos, player:get_player_name()) then + core.record_protection_violation(pos, player:get_player_name()) + return false + end + + local meta = core.get_meta(pos) + local k = 1 + while k <= drawerType do + -- don't add a number in meta fields for 1x1 drawers + local vid = tostring(k) + if drawerType == 1 then vid = '' end + local count = meta:get_int('count' .. vid) + k = k + 1 + if count > 0 then return end + end + + -- drop all drawer upgrades + local upgrades = meta:get_inventory():get_list 'upgrades' + if upgrades then + for _, itemStack in pairs(upgrades) do + if itemStack:get_count() > 0 then return false end + end + end + -- remove node + core.node_dig(pos, node, player) end function drawers.drawer_allow_metadata_inventory_put(pos, listname, index, stack, player) - if core.is_protected(pos, player:get_player_name()) then - core.record_protection_violation(pos, player:get_player_name()) - return 0 - end - if listname ~= "upgrades" then - return 0 - end - if stack:get_count() > 1 then - return 0 - end - if core.get_item_group(stack:get_name(), "drawer_upgrade") < 1 then - return 0 - end - return 1 + if core.is_protected(pos, player:get_player_name()) then + core.record_protection_violation(pos, player:get_player_name()) + return 0 + end + if listname ~= 'upgrades' then return 0 end + if stack:get_count() > 1 then return 0 end + if core.get_item_group(stack:get_name(), 'drawer_upgrade') < 1 then return 0 end + return 1 end function drawers.add_drawer_upgrade(pos, listname, index, stack, player) - -- only do anything if adding to upgrades - if listname ~= "upgrades" then return end + -- only do anything if adding to upgrades + if listname ~= 'upgrades' then return end - drawers.update_drawer_upgrades(pos) + drawers.update_drawer_upgrades(pos) end function drawers.remove_drawer_upgrade(pos, listname, index, stack, player) - -- only do anything if adding to upgrades - if listname ~= "upgrades" then return end + -- only do anything if adding to upgrades + if listname ~= 'upgrades' then return end - drawers.update_drawer_upgrades(pos) + drawers.update_drawer_upgrades(pos) end --[[ Inserts an incoming stack into a specific slot of a drawer. ]] function drawers.drawer_insert_object(pos, stack, visualid) - local visual = drawers.get_visual(pos, visualid) - if not visual then - return stack - end + local visual = drawers.get_visual(pos, visualid) + if not visual then return stack end - return visual:try_insert_stack(stack, true) + return visual:try_insert_stack(stack, true) end --[[ Inserts an incoming stack into a drawer and uses all slots. ]] function drawers.drawer_insert_object_from_tube(pos, node, stack, direction) - local drawer_visuals = drawers.drawer_visuals[core.hash_node_position(pos)] - if not drawer_visuals then - return stack - end - - -- first try to insert in the correct slot (if there are already items) - local leftover = stack - for _, visual in pairs(drawer_visuals) do - if visual.itemName == stack:get_name() then - leftover = visual:try_insert_stack(leftover, true) - end - end - - -- if there's still something left, also use other slots - if leftover:get_count() > 0 then - for _, visual in pairs(drawer_visuals) do - leftover = visual:try_insert_stack(leftover, true) - end - end - return leftover + local drawer_visuals = drawers.drawer_visuals[core.hash_node_position(pos)] + if not drawer_visuals then return stack end + + -- first try to insert in the correct slot (if there are already items) + local leftover = stack + for _, visual in pairs(drawer_visuals) do + if visual.itemName == stack:get_name() then leftover = visual:try_insert_stack(leftover, true) end + end + + -- if there's still something left, also use other slots + if leftover:get_count() > 0 then + for _, visual in pairs(drawer_visuals) do + leftover = visual:try_insert_stack(leftover, true) + end + end + return leftover end --[[ Returns how much (count) of a stack can be inserted to a drawer slot. ]] function drawers.drawer_can_insert_stack(pos, stack, visualid) - local visual = drawers.get_visual(pos, visualid) - if not visual then - return 0 - end + local visual = drawers.get_visual(pos, visualid) + if not visual then return 0 end - return visual:can_insert_stack(stack) + return visual:can_insert_stack(stack) end --[[ Returns whether a stack can be (partially) inserted to any slot of a drawer. ]] function drawers.drawer_can_insert_stack_from_tube(pos, node, stack, direction) - local drawer_visuals = drawers.drawer_visuals[core.hash_node_position(pos)] - if not drawer_visuals then - return false - end - - for _, visual in pairs(drawer_visuals) do - if visual:can_insert_stack(stack) > 0 then - return true - end - end - return false + local drawer_visuals = drawers.drawer_visuals[core.hash_node_position(pos)] + if not drawer_visuals then return false end + + for _, visual in pairs(drawer_visuals) do + if visual:can_insert_stack(stack) > 0 then return true end + end + return false end function drawers.drawer_take_item(pos, itemstack) - local drawer_visuals = drawers.drawer_visuals[core.hash_node_position(pos)] + local drawer_visuals = drawers.drawer_visuals[core.hash_node_position(pos)] - if not drawer_visuals then - return ItemStack("") - end + if not drawer_visuals then return ItemStack '' end - -- check for max count - if itemstack:get_count() > itemstack:get_stack_max() then - itemstack:set_count(itemstack:get_stack_max()) - end + -- check for max count + if itemstack:get_count() > itemstack:get_stack_max() then itemstack:set_count(itemstack:get_stack_max()) end - for _, visual in pairs(drawer_visuals) do - if visual.itemName == itemstack:get_name() then - return visual:take_items(itemstack:get_count()) - end - end + for _, visual in pairs(drawer_visuals) do + if visual.itemName == itemstack:get_name() then return visual:take_items(itemstack:get_count()) end + end - return ItemStack() + return ItemStack() end --[[ Returns the content of a drawer slot. ]] function drawers.drawer_get_content(pos, visualid) - local drawer_meta = core.get_meta(pos) + local drawer_meta = core.get_meta(pos) - return { - name = drawer_meta:get_string("name" .. visualid), - count = drawer_meta:get_int("count" .. visualid), - maxCount = drawer_meta:get_int("max_count" .. visualid) - } + return { + name = drawer_meta:get_string('name' .. visualid), + count = drawer_meta:get_int('count' .. visualid), + maxCount = drawer_meta:get_int('max_count' .. visualid), + } end function drawers.register_drawer(name, def) - def.description = def.description - def.drawtype = "nodebox" - def.node_box = { type = "fixed", fixed = drawers.node_box_simple } - def.collision_box = { type = "regular" } - def.selection_box = { type = "fixed", fixed = drawers.node_box_simple } - def.paramtype = "light" - def.paramtype2 = "colorfacedir" - def.light_source = 10 - def.groups = def.groups or {} - def.is_ground_content = false - - -- events - def.on_construct = drawers.drawer_on_construct - def.on_destruct = drawers.drawer_on_destruct - def.on_dig = drawers.drawer_on_dig - def.allow_metadata_inventory_put = drawers.drawer_allow_metadata_inventory_put - def.allow_metadata_inventory_take = drawers.drawer_allow_metadata_inventory_put - def.on_metadata_inventory_put = drawers.add_drawer_upgrade - def.on_metadata_inventory_take = drawers.remove_drawer_upgrade - - if minetest.get_modpath("screwdriver") and screwdriver then - def.on_rotate = def.on_rotate or screwdriver.disallow - end - - if minetest.get_modpath("pipeworks") and pipeworks then - def.groups.tubedevice = 1 - def.groups.tubedevice_receiver = 1 - def.groups.tubedevice_use_item_entities = 1 - def.tube = def.tube or {} - def.tube.insert_object = def.tube.insert_object or - drawers.drawer_insert_object_from_tube - def.tube.can_insert = def.tube.can_insert or - drawers.drawer_can_insert_stack_from_tube - - def.tube.connect_sides = { - left = 1, - right = 1, - back = 1, - top = 1, - bottom = 1 - } - def.after_place_node = pipeworks.after_place - def.after_dig_node = pipeworks.after_dig - - def.tube.return_input_invref = function(pos, node, dir, owner) - local inv = core.get_meta(pos):get_inventory() -- fakelib.create_inventory() -- fakelib is WEEIRD - local vis = drawers.drawer_visuals[core.hash_node_position(pos)] - if not vis then return false end - for i = 1, 4 do - local this_vis = vis[i] - if not this_vis then break end - - local content = drawers.drawer_get_content(pos, this_vis.visualId) - local stack = ItemStack({ - name = content.name, - count = math.min(content.count, ItemStack(content.name):get_stack_max()) - }) - - inv:set_size("slot" .. i, 1) - inv:set_size("old_slot" .. i, 1) - inv:set_stack("slot" .. i, 1, stack) - inv:set_stack("old_slot" .. i, 1, stack) -- to determine how much has been taken - end - return inv - end - def.tube.after_filter = function(pos, inv) - local vis = drawers.drawer_visuals[core.hash_node_position(pos)] - for i = 1, 4 do - local this_vis = vis[i] - if not this_vis then break end - local stack = inv:get_stack("slot" .. i, 1) - local old_stack = inv:get_stack("old_slot" .. i, 1) - local diff = old_stack:get_count() - stack:get_count() - old_stack:set_count(diff) - drawers.drawer_take_item(pos, old_stack) - -- local content = drawers.drawer_get_content(pos, this_vis.visualId) - -- local count = content.count - diff - end - end - -- def.tube.remove_items = function(pos, node, stack, dir, count, invname, spos, inv) - -- local oldstack = inv:get_stack(invname, 1) - -- oldstack:set_count(oldstack:get_count() - stack:get_count()) - -- inv:set_stack(invname, 1, oldstack) - -- core.debug(inv:get_stack(invname, 1):to_string()) - -- return stack - -- end - - def.tube.ignore_metadata_inventory_take = true - def.tube.input_inventory = { - "slot1", "slot2", "slot3", "slot4" - } - end - def.on_movenode = function(_, to_pos) - minetest.after(0.1, function() - drawers.spawn_visuals(to_pos) - end) - end - def = unifieddyes.def(def, false) - if drawers.enable_1x1 then - -- normal drawer 1x1 = 1 - local def1 = table.copy(def) - def1.tiles = def.tiles or def.tiles1 - def1.tiles1 = nil - def1.tiles2 = nil - def1.tiles4 = nil - def1.groups.drawer = 1 - core.register_node(name .. "1", def1) - core.register_alias(name, name .. "1") -- 1x1 drawer is the default one - end - - if drawers.enable_1x2 then - -- 1x2 = 2 - local def2 = table.copy(def) - def2.description = def.description .. " (1x2)" - def2.tiles = def.tiles2 - def2.tiles1 = nil - def2.tiles2 = nil - def2.tiles4 = nil - def2.groups.drawer = 2 - core.register_node(name .. "2", def2) - end - - if drawers.enable_2x2 then - -- 2x2 = 4 - local def4 = table.copy(def) - def4.description = def.description .. " (2x2)" - def4.tiles = def.tiles4 - def4.tiles1 = nil - def4.tiles2 = nil - def4.tiles4 = nil - def4.groups.drawer = 4 - core.register_node(name .. "4", def4) - end - if (not def.no_craft) and def.material then - if drawers.enable_1x1 then - core.register_craft({ - output = name .. "1", - recipe = { - { def.material, def.material, def.material }, - { "", drawers.CHEST_ITEMSTRING, "" }, - { def.material, def.material, def.material } - } - }) - end - if drawers.enable_1x2 then - core.register_craft({ - output = name .. "2 2", - recipe = { - { def.material, drawers.CHEST_ITEMSTRING, def.material }, - { def.material, def.material, def.material }, - { def.material, drawers.CHEST_ITEMSTRING, def.material } - } - }) - end - if drawers.enable_2x2 then - core.register_craft({ - output = name .. "4 4", - recipe = { - { drawers.CHEST_ITEMSTRING, def.material, drawers.CHEST_ITEMSTRING }, - { def.material, def.material, def.material }, - { drawers.CHEST_ITEMSTRING, def.material, drawers.CHEST_ITEMSTRING } - } - }) - end - end + def.description = def.description + def.drawtype = 'nodebox' + def.node_box = { type = 'fixed', fixed = drawers.node_box_simple } + def.collision_box = { type = 'regular' } + def.selection_box = { type = 'fixed', fixed = drawers.node_box_simple } + def.paramtype = 'light' + def.paramtype2 = 'colorfacedir' + def.light_source = 10 + def.groups = def.groups or {} + def.is_ground_content = false + + -- events + def.on_construct = drawers.drawer_on_construct + def.on_destruct = drawers.drawer_on_destruct + def.on_dig = drawers.drawer_on_dig + def.allow_metadata_inventory_put = drawers.drawer_allow_metadata_inventory_put + def.allow_metadata_inventory_take = drawers.drawer_allow_metadata_inventory_put + def.on_metadata_inventory_put = drawers.add_drawer_upgrade + def.on_metadata_inventory_take = drawers.remove_drawer_upgrade + + if minetest.get_modpath 'screwdriver' and screwdriver then def.on_rotate = def.on_rotate or screwdriver.disallow end + + if minetest.get_modpath 'pipeworks' and pipeworks then + def.groups.tubedevice = 1 + def.groups.tubedevice_receiver = 1 + def.groups.tubedevice_use_item_entities = 1 + def.tube = def.tube or {} + def.tube.insert_object = def.tube.insert_object or drawers.drawer_insert_object_from_tube + def.tube.can_insert = def.tube.can_insert or drawers.drawer_can_insert_stack_from_tube + + def.tube.connect_sides = { + left = 1, + right = 1, + back = 1, + top = 1, + bottom = 1, + } + def.after_place_node = pipeworks.after_place + def.after_dig_node = pipeworks.after_dig + + def.tube.return_input_invref = function(pos, node, dir, owner) + local inv = core.get_meta(pos):get_inventory() -- fakelib.create_inventory() -- fakelib is WEEIRD + local vis = drawers.drawer_visuals[core.hash_node_position(pos)] + if not vis then return false end + for i = 1, 4 do + local this_vis = vis[i] + if not this_vis then break end + + local content = drawers.drawer_get_content(pos, this_vis.visualId) + local stack = ItemStack { + name = content.name, + count = math.min(content.count, ItemStack(content.name):get_stack_max()), + } + + inv:set_size('slot' .. i, 1) + inv:set_size('old_slot' .. i, 1) + inv:set_stack('slot' .. i, 1, stack) + inv:set_stack('old_slot' .. i, 1, stack) -- to determine how much has been taken + end + return inv + end + def.tube.after_filter = function(pos, inv) + local vis = drawers.drawer_visuals[core.hash_node_position(pos)] + for i = 1, 4 do + local this_vis = vis[i] + if not this_vis then break end + local stack = inv:get_stack('slot' .. i, 1) + local old_stack = inv:get_stack('old_slot' .. i, 1) + local diff = old_stack:get_count() - stack:get_count() + old_stack:set_count(diff) + drawers.drawer_take_item(pos, old_stack) + end + end + + def.tube.ignore_metadata_inventory_take = true + def.tube.input_inventory = { + 'slot1', + 'slot2', + 'slot3', + 'slot4', + } + end + def.on_movenode = function(_, to_pos) + minetest.after(0.1, function() + drawers.spawn_visuals(to_pos) + end) + end + def = unifieddyes.def(def, false) + if drawers.enable_1x1 then + -- normal drawer 1x1 = 1 + local def1 = table.copy(def) + def1.tiles = def.tiles or def.tiles1 + def1.tiles1 = nil + def1.tiles2 = nil + def1.tiles4 = nil + def1.groups.drawer = 1 + core.register_node(name .. '1', def1) + core.register_alias(name, name .. '1') -- 1x1 drawer is the default one + end + + if drawers.enable_1x2 then + -- 1x2 = 2 + local def2 = table.copy(def) + def2.description = def.description .. ' (1x2)' + def2.tiles = def.tiles2 + def2.tiles1 = nil + def2.tiles2 = nil + def2.tiles4 = nil + def2.groups.drawer = 2 + core.register_node(name .. '2', def2) + end + + if drawers.enable_2x2 then + -- 2x2 = 4 + local def4 = table.copy(def) + def4.description = def.description .. ' (2x2)' + def4.tiles = def.tiles4 + def4.tiles1 = nil + def4.tiles2 = nil + def4.tiles4 = nil + def4.groups.drawer = 4 + core.register_node(name .. '4', def4) + end + if (not def.no_craft) and def.material then + if drawers.enable_1x1 then + core.register_craft { + output = name .. '1', + recipe = { + { def.material, def.material, def.material }, + { '', drawers.CHEST_ITEMSTRING, '' }, + { def.material, def.material, def.material }, + }, + } + end + if drawers.enable_1x2 then + core.register_craft { + output = name .. '2 2', + recipe = { + { def.material, drawers.CHEST_ITEMSTRING, def.material }, + { def.material, def.material, def.material }, + { def.material, drawers.CHEST_ITEMSTRING, def.material }, + }, + } + end + if drawers.enable_2x2 then + core.register_craft { + output = name .. '4 4', + recipe = { + { drawers.CHEST_ITEMSTRING, def.material, drawers.CHEST_ITEMSTRING }, + { def.material, def.material, def.material }, + { drawers.CHEST_ITEMSTRING, def.material, drawers.CHEST_ITEMSTRING }, + }, + } + end + end end -local template = "drawers:upgrade_template" +local template = 'drawers:upgrade_template' function drawers.register_drawer_upgrade(name, def) - def.groups = def.groups or {} - def.groups.drawer_upgrade = def.groups.drawer_upgrade or 100 - def.inventory_image = def.inventory_image or "drawers_upgrade_template.png" - def.stack_max = 1 - - local recipe_item = def.recipe_item or "air" - def.recipe_item = nil - - core.register_craftitem(name, def) - - if not def.no_craft then - core.register_craft({ - output = name, - recipe = { - { recipe_item, recipe_item, recipe_item }, - { recipe_item, template, recipe_item }, - { recipe_item, recipe_item, recipe_item } - } - }) - template = name - end + def.groups = def.groups or {} + def.groups.drawer_upgrade = def.groups.drawer_upgrade or 100 + def.inventory_image = def.inventory_image or 'drawers_upgrade_template.png' + def.stack_max = 1 + + local recipe_item = def.recipe_item or 'air' + def.recipe_item = nil + + core.register_craftitem(name, def) + + if not def.no_craft then + core.register_craft { + output = name, + recipe = { + { recipe_item, recipe_item, recipe_item }, + { recipe_item, template, recipe_item }, + { recipe_item, recipe_item, recipe_item }, + }, + } + template = name + end end diff --git a/mods/fakelib b/mods/fakelib new file mode 160000 index 00000000..71d82340 --- /dev/null +++ b/mods/fakelib @@ -0,0 +1 @@ +Subproject commit 71d8234040e401e3d575721c48f09c64dc1c5527 diff --git a/mods/fakelib/.gitattributes b/mods/fakelib/.gitattributes deleted file mode 100644 index adcbd9a2..00000000 --- a/mods/fakelib/.gitattributes +++ /dev/null @@ -1,6 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto - -# Exclusions for release export. -.* export-ignore -tests.lua export-ignore diff --git a/mods/fakelib/.github/workflows/luacheck.yml b/mods/fakelib/.github/workflows/luacheck.yml deleted file mode 100644 index 18088bce..00000000 --- a/mods/fakelib/.github/workflows/luacheck.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: luacheck -on: [push, pull_request] -jobs: - luacheck: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@main - - name: Luacheck - uses: lunarmodules/luacheck@master diff --git a/mods/fakelib/API.md b/mods/fakelib/API.md deleted file mode 100644 index 92d3636f..00000000 --- a/mods/fakelib/API.md +++ /dev/null @@ -1,137 +0,0 @@ -# API Documentation - -## Quick Links - -- [`fakelib.is_player(x)`](#fakelibis_playerx) -- [`fakelib.is_metadata(x)`](#fakelibis_metadatax) -- [`fakelib.is_inventory(x)`](#fakelibis_inventoryx) -- [`fakelib.is_vector(x, [add_metatable])`](#fakelibis_vectorx-add_metatable) -- [`fakelib.create_player([options])`](#fakelibcreate_playeroptions) -- [`fakelib.create_inventory([sizes])`](#fakelibcreate_inventorysizes) -- [`fakelib.create_metadata([data])`](#fakelibcreate_metadatadata) - - -## Type checks - -#### **`fakelib.is_player(x)`** - -Checks if a value is a player. Only returns true for real players and `fakelib`'s fake players. - -**Arguments** - -- `x` - Any value. The value to be checked. - -#### **`fakelib.is_inventory(x)`** - -Checks if a value is an inventory. Only returns true for real inventories and `fakelib`'s fake inventories. - -**Arguments** - -- `x` - Any value. The value to be checked. - -#### **`fakelib.is_metadata(x)`** - -Checks if a value is metadata. Only returns true for real metadata and `fakelib`'s fake metadata. - -**Arguments** - -- `x` - Any value. The value to be checked. - -#### **`fakelib.is_vector(x, [add_metatable])`** - -Checks if a value is a vector. Returns true for any table with `x`, `y`, and `z` values that are numbers. - -**Arguments** - -- `x` - Any value. The value to be checked. -- `add_metatable` - Boolean, optional. Add the vector metatable to basic vectors. - - -## Creation - -#### **`fakelib.create_player([options])`** - -Creates a new fake player. - -**Arguments** - -- `options` - Definition table, optional. Specifies player data. See [`options`](#options) below. Can also be a string as shorthand to set the player name only. - -#### **`fakelib.create_inventory([sizes])`** - -Creates a new fake player. - -**Arguments** - -- `sizes` - Definition table, optional. Specifies list names and sizes. See [`sizes`](#sizes) below. - -#### **`fakelib.create_metadata([data])`** - -Creates a new fake player. - -**Arguments** - -- `data` - Definition table, optional. Specifies metadata keys and values. See [`data`](#data) below. - - -## Definition tables. - - -#### **`options`** - -Specifies player data. Used by [`fakelib.create_player([options])`](#fakelibcreate_playeroptions). - -All values are optional. - -- `name` - String. Player name. Unlike real player names, this can contain any characters. -- `position` - Vector. Player position. -- `direction` - Vector. Player look direction. -- `controls` - Table. Player controls. Uses the same format returned by `player:get_player_controls()`. -- `metadata` - Metadata. Player metadata. Can be fake metadata or a reference to real metadata. -- `inventory` - Inventory. Player inventory. Can be a fake inventory or a reference to a real inventory. -- `wield_list` - String. Selected inventory list. Must be a list that exists in the player's inventory. -- `wield_index` - Number. Selected list index. Must be an index that exists in the selected list. - -Example: -```lua -local options = { - name = "sam", - position = vector.new(1, 5, 3), - direction = vector(1, 0, 0), - controls = {sneak = true}, -} -local player = fakelib.create_player(options) -``` - -#### **`sizes`** - -Specifies list names and sizes. Used by [`fakelib.create_inventory([sizes])`](#fakelibcreate_inventorysizes). - -List names must be strings, and list sizes must be numbers greater than zero. - -Example: -```lua -local sizes = { - main = 32, - craft = 9, - craftpreview = 1, - craftresult = 1, -} -local inv = fakelib.create_inventory(sizes) - -``` - -#### **`data`** - -Specifies metadata keys and values. Used by [`fakelib.create_metadata([data])`](#fakelibcreate_metadatadata). - -Keys must be strings, and values must be strings or numbers. - -Example: -```lua -local data = { - enabled = "true", - energy = 300, -} -local meta = fakelib.create_metadata(data) -``` \ No newline at end of file diff --git a/mods/fakelib/LICENSE b/mods/fakelib/LICENSE deleted file mode 100644 index 20046852..00000000 --- a/mods/fakelib/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 OgelGames - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/mods/fakelib/README.md b/mods/fakelib/README.md deleted file mode 100644 index eac86277..00000000 --- a/mods/fakelib/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Minetest fake userdata library [fakelib] - -[![luacheck](https://github.com/OgelGames/fakelib/workflows/luacheck/badge.svg)](https://github.com/OgelGames/fakelib/actions) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE.md) -[![Minetest](https://img.shields.io/badge/Minetest-5.0+-blue.svg)](https://www.minetest.net) -[![ContentDB](https://content.minetest.net/packages/OgelGames/fakelib/shields/downloads/)](https://content.minetest.net/packages/OgelGames/fakelib/) - -## Overview - -This mod is a code library for creating fake userdata (players, inventories and metadata), replicating their functionality as closely as possible. - -## Usage - -Simply add `fakelib` to your mod's dependencies, and call any of the API functions from your code. - -See [API.md](API.md) for detailed documentation. - -## Installation - -Download the [master branch](https://github.com/OgelGames/fakelib/archive/master.zip) or the [latest release](https://github.com/OgelGames/fakelib/releases), and follow [the usual installation steps](https://wiki.minetest.net/Installing_Mods). - -Alternatively, you can download and install the mod from [ContentDB](https://content.minetest.net/packages/OgelGames/fakelib) or the online content tab in Minetest. - -## License - -All code is licensed under the [MIT License](LICENSE). diff --git a/mods/fakelib/init.lua b/mods/fakelib/init.lua deleted file mode 100644 index ef0cf9d5..00000000 --- a/mods/fakelib/init.lua +++ /dev/null @@ -1,53 +0,0 @@ - -fakelib = {} - -local function check(n, v, a, b) - local t = type(v) - if t == a or t == b then - return v - end - local info = debug.getinfo(2, "n") - local f = info.name or "?" - if info.namewhat ~= "method" then - -- Offset argument number when called using '.' instead of ':' - n = n + 1 - end - error(string.format("bad argument #%i to '%s' (%s expected, got %s)", n, f, a, t), 3) -end - -local function secure_table(t, index, id) - setmetatable(t, { - __index = index, - __newindex = {}, - __metatable = id, - }) - return t -end - -local path = minetest.get_modpath("fakelib") - -for _,file in pairs({"metadata", "inventory", "player"}) do - loadfile(path.."/"..file..".lua")(check, secure_table) -end - -dofile(path.."/misc.lua") - --- Tests are not included in releases, so check for them before registering the command. - -local tests = loadfile(path.."/tests.lua") - -if tests and minetest.is_singleplayer() then - minetest.register_chatcommand("fakelib_test", { - description = "Test fakelib's API.", - params = "[]", - func = function(_, param) - local start_time = minetest.get_us_time() - local success = tests(param == "true") - local end_time = minetest.get_us_time() - if success then - return true, string.format("Testing completed in %i us", end_time - start_time) - end - return true, "Testing failed. See console for errors." - end, - }) -end diff --git a/mods/fakelib/inventory.lua b/mods/fakelib/inventory.lua deleted file mode 100644 index 64cfde4d..00000000 --- a/mods/fakelib/inventory.lua +++ /dev/null @@ -1,301 +0,0 @@ -local fake_inventory = {} -local identifier = "fakelib:inventory" -local check, secure_table = ... - --- API functions ----------------------------------------- - -function fakelib.is_inventory(x) - if type(x) == "userdata" and x.get_lists then - return true - elseif type(x) == "table" and getmetatable(x) == identifier then - return true - end - return false -end - -function fakelib.create_inventory(sizes) - local lists = {} - if type(sizes) == "table" then - for listname, size in pairs(sizes) do - if type(listname) == "string" and type(size) == "number" and size > 0 then - local list = {} - for i = 1, size do - list[i] = ItemStack() - end - lists[listname] = list - end - end - end - return secure_table({ lists = lists }, fake_inventory, identifier) -end - --- Helper functions ----------------------------------------- - -local function copy_list(list) - local copy = {} - for i = 1, #list do - copy[i] = ItemStack(list[i]) - end - return copy -end - -local function stack_matches(a, b, match_meta) - if a:get_name() ~= b:get_name() then - return false - end - if match_meta then - if a:get_wear() ~= b:get_wear() then - return false - end - return a:get_meta():equals(b:get_meta()) - end - return true -end - --- Inventory functions ----------------------------------------- - -function fake_inventory:is_empty(listname) - check(1, listname, "string", "number") - local list = self.lists[tostring(listname)] - if not list or #list == 0 then - return true - end - for _, stack in ipairs(list) do - if not stack:is_empty() then - return false - end - end - return true -end - -function fake_inventory:get_size(listname) - check(1, listname, "string", "number") - local list = self.lists[tostring(listname)] - return list and #list or 0 -end - -function fake_inventory:set_size(listname, size) - check(1, listname, "string", "number") - check(2, size, "number") - listname = tostring(listname) - if size ~= size or size < 0 then - return false - end - size = math.floor(size) - if size == 0 then - self.lists[listname] = nil - return true - end - local list = self.lists[listname] or {} - if #list < size then - for i = #list + 1, size do - list[i] = ItemStack() - end - elseif #list > size then - for i = size + 1, #list do - list[i] = nil - end - end - self.lists[listname] = list - return true -end - -function fake_inventory:get_width(listname) - check(1, listname, "string", "number") - local list = self.lists[tostring(listname)] - return list and list.width or 0 -end - -function fake_inventory:set_width(listname, width) - check(1, listname, "string", "number") - check(2, width, "number") - local list = self.lists[tostring(listname)] - if not list or width ~= width or width < 0 then - return false - end - width = math.floor(width) - list.width = width > 0 and width or nil - return true -end - -function fake_inventory:get_stack(listname, i) - check(1, listname, "string", "number") - check(2, i, "number") - i = math.floor(i) - local list = self.lists[tostring(listname)] - if not list or not list[i] then - return ItemStack() - end - return ItemStack(list[i]) -end - -function fake_inventory:set_stack(listname, i, stack) - check(1, listname, "string", "number") - check(2, i, "number") - stack = ItemStack(stack) - i = math.floor(i) - local list = self.lists[tostring(listname)] - if not list or not list[i] or stack:is_empty() then - return false - end - list[i] = stack - return true -end - -function fake_inventory:get_list(listname) - check(1, listname, "string", "number") - local list = self.lists[tostring(listname)] - return list and copy_list(list) or nil -end - -function fake_inventory:set_list(listname, list) - check(1, listname, "string", "number") - listname = tostring(listname) - if list == nil then - self.lists[listname] = nil - return - end - check(2, list, "table") - local new_list, size = {}, 0 - for i, s in pairs(list) do - check(4, i, "number") - if i > size then - size = i - end - new_list[i] = ItemStack(s) - end - for i = 1, size do - if not new_list[i] then - new_list[i] = ItemStack() - end - end - self.lists[listname] = new_list -end - -function fake_inventory:get_lists() - local lists = {} - for listname, list in pairs(self.lists) do - lists[listname] = copy_list(list) - end - return lists -end - -function fake_inventory:set_lists(lists) - check(1, lists, "table") - local new_lists = {} - for listname, list in pairs(lists) do - check(3, listname, "string", "number") - check(3, list, "table") - listname = tostring(listname) - local new_list, size = {}, 0 - for i, s in pairs(list) do - check(5, i, "number") - if i > size then - size = i - end - new_list[i] = ItemStack(s) - end - for i = 1, size do - if not new_list[i] then - new_list[i] = ItemStack() - end - end - new_lists[listname] = new_list - end - self.lists = new_lists -end - -function fake_inventory:add_item(listname, stack) - check(1, listname, "string", "number") - stack = ItemStack(stack) - local list = self.lists[tostring(listname)] - if not list or #list == 0 or stack:is_empty() then - return stack - end - local empty = {} - for _, s in ipairs(list) do - if s:is_empty() then - table.insert(empty, s) - else - stack = s:add_item(stack) - if stack:is_empty() then - return stack - end - end - end - for _, s in ipairs(empty) do - stack = s:add_item(stack) - if stack:is_empty() then - return stack - end - end - return stack -end - -function fake_inventory:room_for_item(listname, stack) - check(1, listname, "string", "number") - stack = ItemStack(stack) - local list = self.lists[tostring(listname)] - if not list or #list == 0 or stack:is_empty() then - return false - end - for _, s in ipairs(copy_list(list)) do - stack = s:add_item(stack) - if stack:is_empty() then - return true - end - end - return false -end - -function fake_inventory:contains_item(listname, stack, match_meta) - check(1, listname, "string", "number") - stack = ItemStack(stack) - local list = self.lists[tostring(listname)] - if not list or stack:is_empty() or stack:is_empty() then - return false - end - local count = stack:get_count() - for _, s in ipairs(list) do - if stack_matches(stack, s, match_meta) then - count = count - s:get_count() - if count <= 0 then - return true - end - end - end - return false -end - -function fake_inventory:remove_item(listname, stack) - check(1, listname, "string", "number") - stack = ItemStack(stack) - local list = self.lists[tostring(listname)] - if not list or #list == 0 or stack:is_empty() then - return ItemStack() - end - local name, remaining, removed = stack:get_name(), stack:get_count() - for i = #list, 1, -1 do - local s = list[i] - if s:get_name() == name then - s = s:take_item(remaining) - remaining = remaining - s:get_count() - if not removed then - removed = s - else - removed:set_count(removed:get_count() + s:get_count()) - end - if remaining == 0 then - break - end - end - end - return removed or ItemStack() -end - -function fake_inventory.get_location() - return { type = "undefined" } -end diff --git a/mods/fakelib/metadata.lua b/mods/fakelib/metadata.lua deleted file mode 100644 index cfcde2a1..00000000 --- a/mods/fakelib/metadata.lua +++ /dev/null @@ -1,134 +0,0 @@ - -local fake_metadata = {} -local identifier = "fakelib:metadata" -local check, secure_table = ... - --- API functions ----------------------------------------- - -function fakelib.is_metadata(x) - if type(x) == "userdata" and x.get_keys then - return true - elseif type(x) == "table" and getmetatable(x) == identifier then - return true - end - return false -end - -function fakelib.create_metadata(data) - local fields = {} - if type(data) == "table" then - for k,v in pairs(data) do - if type(k) == "string" and type(v) == "string" then - fields[k] = v - end - end - end - return secure_table({fields = fields}, fake_metadata, identifier) -end - --- Metadata functions ----------------------------------------- - -function fake_metadata:contains(key) - check(1, key, "string", "number") - key = tostring(key) - return self.fields[key] ~= nil -end - -function fake_metadata:get(key) - check(1, key, "string", "number") - key = tostring(key) - return self.fields[key] -end - -function fake_metadata:set_string(key, value) - check(1, key, "string", "number") - check(2, value, "string", "number") - key = tostring(key) - value = tostring(value) - if value == "" then - self.fields[key] = nil - end - self.fields[key] = value -end - -function fake_metadata:get_string(key) - check(1, key, "string", "number") - key = tostring(key) - return self.fields[key] or "" -end - -function fake_metadata:set_int(key, value) - check(1, key, "string", "number") - check(2, value, "number") - key = tostring(key) - if value >= 2^31 then - value = 0 - end - self.fields[key] = string.format("%i", value) -end - -function fake_metadata:get_int(key) - check(1, key, "string", "number") - key = tostring(key) - return tonumber(self.fields[key]) or 0 -end - -function fake_metadata:set_float(key, value) - check(1, key, "string", "number") - check(2, value, "number") - key = tostring(key) - self.fields[key] = string.format("%s", value) -end - -function fake_metadata:get_float(key) - check(1, key, "string", "number") - key = tostring(key) - return tonumber(self.fields[key]) or 0 -end - -function fake_metadata:get_keys() - local keys = {} - for key in pairs(self.fields) do - table.insert(keys, key) - end - return keys -end - -function fake_metadata:to_table() - return {fields = table.copy(self.fields)} -end - -function fake_metadata:from_table(data) - if type(data) ~= "table" or type(data.fields) ~= "table" then - self.fields = {} - return true - end - local fields = {} - for k,v in pairs(data.fields) do - check(4, k, "string") - check(5, v, "string", "number") - fields[k] = tostring(v) - end - self.fields = fields - return true -end - -function fake_metadata:equals(other) - if not fakelib.is_metadata(other) then - check(1, other, "MetaDataRef") - end - local fields = other:to_table().fields - for k,v in pairs(self.fields) do - if fields[k] == v then - fields[k] = nil - elseif fields[k] ~= nil then - return false - end - end - if next(fields) == nil then - return true - end - return false -end diff --git a/mods/fakelib/misc.lua b/mods/fakelib/misc.lua deleted file mode 100644 index ece4c528..00000000 --- a/mods/fakelib/misc.lua +++ /dev/null @@ -1,10 +0,0 @@ - -function fakelib.is_vector(x, add_metatable) - if type(x) ~= "table" or type(x.x) ~= "number" or type(x.y) ~= "number" or type(x.z) ~= "number" then - return false - end - if add_metatable and getmetatable(x) ~= vector.metatable then - setmetatable(x, vector.metatable) - end - return true -end diff --git a/mods/fakelib/mod.conf b/mods/fakelib/mod.conf deleted file mode 100644 index 19784a68..00000000 --- a/mods/fakelib/mod.conf +++ /dev/null @@ -1,3 +0,0 @@ -name = fakelib -description = Minetest fake userdata library -min_minetest_version = 5.0 diff --git a/mods/fakelib/player.lua b/mods/fakelib/player.lua deleted file mode 100644 index 28e816ce..00000000 --- a/mods/fakelib/player.lua +++ /dev/null @@ -1,496 +0,0 @@ - -local fake_player = {is_fake_player = true} -local identifier = "fakelib:player" -local check, secure_table = ... - -local player_controls = { - up = 1, down = 2, left = 4, right = 8, jump = 16, - aux1 = 32, sneak = 64, dig = 128, place = 256, zoom = 512, -} - --- API functions ----------------------------------------- - -function fakelib.is_player(x) - if type(x) == "userdata" and x.is_player and x:is_player() then - return true - elseif type(x) == "table" and getmetatable(x) == identifier then - return true - end - return false -end - -function fakelib.create_player(options) - local data = {} - if type(options) == "table" then - if type(options.name) == "string" then - data.name = options.name - end - if fakelib.is_vector(options.position) then - data.position = vector.copy(options.position) - end - if fakelib.is_vector(options.direction) then - local dir = vector.normalize(options.direction) - data.pitch = -math.asin(dir.y) - data.yaw = math.atan2(-dir.x, dir.z) % (math.pi * 2) - end - if type(options.controls) == "table" then - data.controls = {} - for name in pairs(player_controls) do - data.controls[name] = options.controls[name] == true - end - data.controls.dig = data.controls.dig or options.controls.LMB - data.controls.place = data.controls.place or options.controls.RMB - end - if fakelib.is_metadata(options.metadata) then - data.metadata = options.metadata - end - if fakelib.is_inventory(options.inventory) then - data.inventory = options.inventory - end - local size = 32 - if data.inventory and type(options.wield_list) == "string" then - size = data.inventory:get_size(options.wield_list) - if size > 0 then - data.wield_list = options.wield_list - end - end - if type(options.wield_index) == "number" then - if options.wield_index > 0 and options.wield_index <= size then - data.wield_index = options.wield_index - end - end - elseif type(options) == "string" then - data.name = options - end - return secure_table({data = data}, fake_player, identifier) -end - --- Helper functions ----------------------------------------- - -local function check_vector(v) - local t = type(v) - if t ~= "table" then - error(string.format("\"Invalid vector (expected table got %s).\"", t), 3) - end - for _,c in ipairs({"x", "y", "z"}) do - t = type(v[c]) - if t ~= "number" then - error(string.format("\"Invalid vector coordinate %s (expected number got %s).\"", c, t), 3) - end - end -end - --- Dynamic get/set functions ----------------------------------------- - -function fake_player:get_player_name() - return self.data.name or "" -end - -function fake_player:get_inventory() - if not self.data.inventory then - self.data.inventory = fakelib.create_inventory({ - main = 32, craft = 9, craftpreview = 1, craftresult = 1 - }) - end - return self.data.inventory -end - -function fake_player:get_meta() - if not self.data.metadata then - self.data.metadata = fakelib:create_metadata() - end - return self.data.metadata -end - -function fake_player:get_look_dir() - local p, y = self.data.pitch or 0, self.data.yaw or 0 - return vector.new(math.sin(-y) * math.cos(p), math.sin(-p), math.cos(y) * math.cos(p)) -end - -function fake_player:get_look_horizontal() - return self.data.yaw or 0 -end - -function fake_player:set_look_horizontal(value) - check(1, value, "number") - self.data.yaw = value % (math.pi * 2) -end - -function fake_player:get_look_vertical() - return self.data.pitch or 0 -end - -function fake_player:set_look_vertical(value) - check(1, value, "number") - self.data.pitch = math.max(-math.pi / 2, math.min(value, math.pi / 2)) -end - -function fake_player:get_player_control() - local controls = {} - if self.data.controls then - for name in pairs(player_controls) do - controls[name] = self.data.controls[name] - end - else - for name in pairs(player_controls) do - controls[name] = false - end - end - controls.LMB = controls.dig - controls.RMB = controls.place - return controls -end - -function fake_player:get_player_control_bits() - if not self.data.controls then - return 0 - end - local total = 0 - for name, value in pairs(player_controls) do - total = total + self.data.controls[name] and value or 0 - end - return total -end - -function fake_player:get_pos() - if self.data.position then - return vector.copy(self.data.position) - end - return vector.zero() -end - -function fake_player:set_pos(pos) - check_vector(pos) - self.data.position = vector.copy(pos) -end -fake_player.move_to = fake_player.set_pos - -function fake_player:add_pos(pos) - check_vector(pos) - if self.data.position then - self.data.position = vector.add(self.data.position, pos) - else - self.data.position = vector.copy(pos) - end -end - -function fake_player:get_wield_index() - return self.data.wield_index or 1 -end - -function fake_player:get_wield_list() - return self.data.wield_list or "main" -end - -function fake_player:get_wielded_item() - if self.data.inventory then - return self.data.inventory:get_stack(self:get_wield_list(), self:get_wield_index()) - end - return ItemStack() -end - -function fake_player:set_wielded_item(stack) - stack = ItemStack(stack) - if not self.data.inventory and stack:is_empty() then - return true - end - self:get_inventory():set_stack(self:get_wield_list(), self:get_wield_index(), stack) - return true -end - --- Static get functions ----------------------------------------- - -function fake_player.is_player() - return true -end - -function fake_player.get_animation() - return {x = 1, y = 1}, 15, 0, true -end - -function fake_player.get_armor_groups() - return {immortal = 1} -end - -function fake_player.get_bone_override() - return { - position = {absolute = false, vec = vector.zero(), interpolation = 0}, - rotation = {absolute = false, vec = vector.zero(), interpolation = 0}, - scale = {absolute = false, vec = vector.new(1, 1, 1), interpolation = 0}, - } -end - -function fake_player.get_bone_overrides() - return {} -end - -function fake_player.get_bone_position() - return vector.zero(), vector.zero() -end - -function fake_player.get_breath() - return 10 -end - -function fake_player.get_children() - return {} -end - -function fake_player.get_clouds() - return { - ambient = {r = 0, g = 0, b = 0, a = 255}, - color = {r = 240, g = 240, b = 255, a = 229}, - density = 0.4, - height = 120, - speed = {x = 0, y = -2}, - thickness = 16, - } -end - -function fake_player.get_eye_offset() - return vector.zero(), vector.zero(), vector.zero() -end - -function fake_player.get_formspec_prepend() - return "" -end - -function fake_player.get_fov() - return 0, false, 0 -end - -function fake_player.get_hp() - return 20 -end - -function fake_player.get_inventory_formspec() - return "" -end - -function fake_player.get_lighting() - return { - exposure = { - speed_bright_dark = 1000, - center_weight_power = 1, - luminance_min = -3, - luminance_max = -3, - exposure_correction = 0, - speed_dark_bright = 1000 - }, - saturation = 1, - shadows = {intensity = 0}, - volumetric_light = {strength = 0}, - } -end - -function fake_player.get_local_animation() - return {x = 0, y = 0}, {x = 0, y = 0}, {x = 0, y = 0}, {x = 0, y = 0}, 0 -end - -function fake_player.get_moon() - return { - scale = 1, - texture = "moon.png", - tonemap = "moon_tonemap.png", - visible = true, - } -end - -function fake_player.get_nametag_attributes() - return { - bgcolor = false, - color = {r = 255, g = 255, b = 255, a = 255}, - text = "", - } -end - -function fake_player.get_physics_override() - return { - acceleration_air = 1, acceleration_default = 1, acceleration_fast = 1, - gravity = 1, jump = 1, speed = 1, - liquid_fluidity = 1, liquid_fluidity_smooth = 1, liquid_sink = 1, - speed_climb = 1, speed_crouch = 1, speed_fast = 1, speed_walk = 1, - new_move = true, sneak = true, sneak_glitch = false, - } -end - -function fake_player.get_properties() - return { - automatic_face_movement_dir = false, - automatic_face_movement_max_rotation_per_sec = -1, - automatic_rotate = 0, - backface_culling = false, - breath_max = 10, - collide_with_objects = true, - collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, - colors = {{r = 255, g = 255, b = 255, a = 255}}, - damage_texture_modifier = "^[brighten", - eye_height = 0, - glow = 0, - hp_max = 20, - infotext = "", - initial_sprite_basepos = {x = 0, y = 0}, - is_visible = true, - makes_footstep_sound = true, - mesh = "", - nametag = "", - nametag_bgcolor = false, - nametag_color = {r = 255, g = 255, b = 255, a = 255}, - physical = false, - pointable = true, - selectionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5, rotate = false}, - shaded = true, - show_on_minimap = true, - spritediv = {x = 1, y = 1}, - static_save = true, - stepheight = 0.6, - textures = {"blank.png"}, - use_texture_alpha = false, - visual = "cube", - visual_size = vector.new(1, 1, 1), - wield_item = "", - zoom_fov = 15, - } -end - -function fake_player.get_sky_color() - return fake_player.get_sky(true).sky_color -end - -function fake_player.get_sky(as_table) - if as_table then - return { - base_color = {r = 255, g = 255, b = 255, a = 255}, - clouds = true, - fog = {fog_distance = -1, fog_start = -1}, - sky_color = { - day_sky = {r = 97, g = 181, b = 245, a = 255}, - day_horizon = {r = 144, g = 211, b = 246, a = 255}, - dawn_sky = {r = 180, g = 186, b = 250, a = 255}, - dawn_horizon = {r = 186, g = 193, b = 240, a = 255}, - night_sky = {r = 0, g = 107, b = 255, a = 255}, - night_horizon = {r = 64, g = 144, b = 255, a = 255}, - indoors = {r = 100, g = 100, b = 100, a = 255}, - fog_sun_tint = {r = 244, g = 125, b = 29, a = 255}, - fog_moon_tint = {r = 128, g = 153, b = 204, a = 255}, - fog_tint_type = "default", - }, - textures = {}, - type = "regular", - } - end - return {r = 255, g = 255, b = 255, a = 255}, "regular", {}, true -end - -function fake_player.get_stars() - return { - count = 1000, - day_opacity = 0, - scale = 1, - star_color = {r = 235, g = 235, b = 255, a = 105}, - visible = true, - } -end - -function fake_player.get_sun() - return { - scale = 1, - sunrise = "sunrisebg.png", - sunrise_visible = true, - texture = "sun.png", - tonemap = "sun_tonemap.png", - visible = true, - } -end - -function fake_player.get_velocity() - return vector.zero() -end - -function fake_player.hud_get_all() - return {} -end - -function fake_player.hud_get_flags() - return { - basic_debug = false, - breathbar = false, - chat = false, - crosshair = false, - healthbar = false, - hotbar = false, - minimap = false, - minimap_radar = false, - wielditem = false, - } -end - -function fake_player.hud_get_hotbar_image() - return "" -end - -function fake_player.hud_get_hotbar_itemcount() - return 8 -end - -function fake_player.hud_get_hotbar_selected_image() - return "" -end - -function fake_player.is_valid() - return true -end - --- No-op functions ----------------------------------------- -do - local functions = { - -- Lua entity only (no-op for players) - "get_acceleration", "get_entity_name", "get_luaentity", "get_rotation", - "get_texture_mod", "get_yaw", "getacceleration", "getyaw", "remove", - "set_acceleration", "set_rotation", "set_sprite", "set_texture_mod", - "set_velocity", "set_yaw", "setacceleration", "setsprite", - "settexturemod", "setvelocity", "setyaw", - -- Non-functional get/set functions - "add_velocity", "get_attach", "get_attribute", "get_day_night_ratio", - "hud_add", "hud_change", "hud_get", "hud_remove", "hud_set_flags", - "hud_set_hotbar_image", "hud_set_hotbar_itemcount", - "hud_set_hotbar_selected_image", "override_day_night_ratio", - "set_animation", "set_animation_frame_speed", "set_armor_groups", - "set_attach", "set_attribute", "set_bone_override", "set_bone_position", - "set_breath", "set_clouds", "set_detach", "set_eye_offset", - "set_formspec_prepend", "set_fov", "set_hp", "set_inventory_formspec", - "set_lighting", "set_local_animation", "set_minimap_modes", "set_moon", - "set_nametag_attributes", "set_physics_override", - "set_properties", "set_sky", "set_stars", "set_sun", - -- Other functions that do nothing - "punch", "respawn", "right_click", "send_mapblock", - } - for _,func in ipairs(functions) do - fake_player[func] = function() end - end -end - --- Deprecated functions ----------------------------------------- - -function fake_player:get_look_pitch() - return self:get_look_vertical() * -1 -end - -function fake_player:get_look_yaw() - return self:get_look_horizontal() + math.pi / 2 -end - -fake_player.set_look_pitch = fake_player.set_look_vertical -fake_player.set_look_yaw = fake_player.set_look_horizontal -fake_player.getpos = fake_player.get_pos -fake_player.setpos = fake_player.set_pos -fake_player.moveto = fake_player.set_pos -fake_player.getvelocity = fake_player.get_velocity -fake_player.add_player_velocity = fake_player.add_velocity -fake_player.get_player_velocity = fake_player.get_velocity diff --git a/mods/fakelib/tests.lua b/mods/fakelib/tests.lua deleted file mode 100644 index ef163b6c..00000000 --- a/mods/fakelib/tests.lua +++ /dev/null @@ -1,324 +0,0 @@ - -local error_tests = ... -local success = true - --- Helper functions ------------------------------- - -local function round(x) - if x >= 0 then - return math.floor(x + 0.5) - end - return math.ceil(x - 0.5) -end - -local function is_equal(a, b) - local ta, tb = type(a), type(b) - -- Compare table values recursively - if ta == "table" and tb == "table" then - if next(a) == nil and next(b) == nil then - return true - end - local keys = {} - for k in pairs(a) do - keys[k] = true - end - for k in pairs(b) do - keys[k] = true - end - for k in pairs(keys) do - if not is_equal(a[k], b[k]) then - return false - end - end - return true - end - -- Get rid of floating point errors - if ta == "number" and tb == "number" then - a = round(a * 1000) / 1000 - b = round(b * 1000) / 1000 - end - return a == b -end - -local function test(real, fake, func, ...) - local rvals_real = {pcall(real[func], real, ...)} - local rvals_fake = {pcall(fake[func], fake, ...)} - local i = 1 - while true do - if rvals_real[i] == nil and rvals_fake[i] == nil then - break - end - if not is_equal(rvals_real[i], rvals_fake[i]) then - success = false - print(string.format("[fakelib] Test failed with function '%s' and return value #%i", func, i-1)) - if #{...} > 0 then - print(" > Function arguments: ", ...) - end - local a = type(rvals_real[i]) == "table" and dump(rvals_real[i]) or tostring(rvals_real[i]) - print(string.format(" > Real return value: %s", a)) - local b = type(rvals_fake[i]) == "table" and dump(rvals_fake[i]) or tostring(rvals_fake[i]) - print(string.format(" > Fake return value: %s", b)) - end - i = i + 1 - end -end - -local test_values = {"abc", "", 0, -1, {}, {true}, true, function() end, 0/0, nil} - -local function iter_values() - local i = 0 - return function () - i = i + 1 - if i <= 1 then return test_values[i] end - end -end - --- Player tests ------------------------------- - -local real_player = minetest.get_player_by_name("singleplayer") -local fake_player = fakelib.create_player("singleplayer") - --- Type check -if not fakelib.is_player(real_player) then - success = false - print("[fakelib] Real player is not a player.") -end -if not fakelib.is_player(fake_player) then - success = false - print("[fakelib] Fake player is not a player.") -end -for value in iter_values() do - if fakelib.is_player(value) then - print(string.format("[fakelib] Value '%s' is a player.", tostring(value))) - end -end - --- Function check -for func in pairs(getmetatable(real_player)) do - if not fake_player[func] then - success = false - print(string.format("[fakelib] Missing function '%s'", func)) - end -end - --- Functional tests -test(real_player, fake_player, "get_player_name") -test(real_player, fake_player, "get_player_control") -test(real_player, fake_player, "get_player_control_bits") -test(real_player, fake_player, "set_pos", vector.zero()) -test(real_player, fake_player, "add_pos", vector.new(1, 1, 1)) -test(real_player, fake_player, "get_pos") -test(real_player, fake_player, "set_look_horizontal", 0.6) -test(real_player, fake_player, "set_look_vertical", 0.3) -test(real_player, fake_player, "get_look_dir") -test(real_player, fake_player, "get_look_horizontal") -test(real_player, fake_player, "get_look_pitch") -test(real_player, fake_player, "get_look_yaw") -test(real_player, fake_player, "set_wielded_item", ItemStack("air 99")) -test(real_player, fake_player, "get_wielded_item") - --- Error tests -if error_tests then - for value in iter_values() do - -- Only test dynamic functions, no-op functions don't check arguments - test(real_player, fake_player, "set_pos", value) - test(real_player, fake_player, "add_pos", value) - test(real_player, fake_player, "set_look_horizontal", value) - test(real_player, fake_player, "set_look_vertical", value) - test(real_player, fake_player, "set_wielded_item", value) - end -end - --- Inventory tests ------------------------------- - -local real_inv = real_player:get_inventory() -local fake_inv = fakelib.create_inventory() - --- Type check -if not fakelib.is_inventory(real_inv) then - success = false - print("[fakelib] Real inventory is not an inventory.") -end -if not fakelib.is_inventory(fake_inv) then - success = false - print("[fakelib] Fake inventory is not an inventory.") -end -for value in iter_values() do - if fakelib.is_inventory(value) then - print(string.format("[fakelib] Value '%s' is an inventory.", tostring(value))) - end -end - --- Function check -for func in pairs(getmetatable(real_inv)) do - if not fake_inv[func] then - success = false - print(string.format("[fakelib] Missing function '%s'", func)) - end -end - --- Save and clear -local old_inv = real_inv:get_lists() -real_inv:set_lists({}) - -local test_item = ItemStack({name = "air", meta = {test = "abc"}}) -local test_list = {"", "air 50", test_item, "", "air 50", test_item} - --- Functional tests -test(real_inv, fake_inv, "set_list", "test", test_list) -test(real_inv, fake_inv, "is_empty", "test") -test(real_inv, fake_inv, "set_lists", {test = test_list}) -test(real_inv, fake_inv, "set_size", "test", 10) -test(real_inv, fake_inv, "get_size", "test") -test(real_inv, fake_inv, "set_width", "test", 3) -test(real_inv, fake_inv, "get_width", "test") -test(real_inv, fake_inv, "set_stack", "test", 1.5, "air") -test(real_inv, fake_inv, "get_stack", "test", 1) -test(real_inv, fake_inv, "get_list", "test") -test(real_inv, fake_inv, "add_item", "test", "air") -test(real_inv, fake_inv, "room_for_item", "test", "air 99") -test(real_inv, fake_inv, "contains_item", "test", "air 99") -test(real_inv, fake_inv, "contains_item", "test", test_item, true) -test(real_inv, fake_inv, "remove_item", "test", "air 99") -test(real_inv, fake_inv, "get_lists") - --- Error tests -if error_tests then - for value in iter_values() do - test(real_inv, fake_inv, "set_list", value, {}) - test(real_inv, fake_inv, "set_list", "test", value) - test(real_inv, fake_inv, "is_empty", value) - test(real_inv, fake_inv, "set_lists", value) - test(real_inv, fake_inv, "set_size", value, 1) - test(real_inv, fake_inv, "set_size", "test", value) - test(real_inv, fake_inv, "get_size", value) - test(real_inv, fake_inv, "set_width", value, 1) - test(real_inv, fake_inv, "set_width", "test", value) - test(real_inv, fake_inv, "get_width", value) - test(real_inv, fake_inv, "set_stack", value, 1, "test") - test(real_inv, fake_inv, "set_stack", "test", value, "test") - test(real_inv, fake_inv, "set_stack", "test", 1, value) - test(real_inv, fake_inv, "get_stack", value, 1) - test(real_inv, fake_inv, "get_stack", "test", value) - test(real_inv, fake_inv, "get_list", value) - test(real_inv, fake_inv, "add_item", value, "test") - test(real_inv, fake_inv, "add_item", "test", value) - test(real_inv, fake_inv, "room_for_item", value, "test") - test(real_inv, fake_inv, "room_for_item", "test", value) - test(real_inv, fake_inv, "contains_item", value, "test") - test(real_inv, fake_inv, "contains_item", "test", value) - test(real_inv, fake_inv, "contains_item", "test", "test", value) - test(real_inv, fake_inv, "remove_item", value, "test") - test(real_inv, fake_inv, "remove_item", "test", value) - end -end - --- Reset -real_inv:set_lists(old_inv) - - --- Metadata tests ------------------------------- - -local real_meta = real_player:get_meta() -local fake_meta = fakelib.create_metadata() - --- Type check -if not fakelib.is_metadata(real_meta) then - success = false - print("[fakelib] Real metadata is not metadata.") -end -if not fakelib.is_metadata(fake_meta) then - success = false - print("[fakelib] Fake metadata is not metadata.") -end -for value in iter_values() do - if fakelib.is_metadata(value) then - print(string.format("[fakelib] Value '%s' is metadata.", tostring(value))) - end -end - --- Function check -for func in pairs(getmetatable(real_meta)) do - if not fake_meta[func] then - success = false - print(string.format("[fakelib] Missing function '%s'", func)) - end -end - --- Save and clear -local old_meta = real_meta:to_table() -real_meta:from_table({}) - -local test_data = {fields = {test = "abc"}} - --- Functional tests -test(real_meta, fake_meta, "from_table", test_data) -test(real_meta, fake_meta, "contains", "test") -test(real_meta, fake_meta, "get", "test") -test(real_meta, fake_meta, "set_string", "test", "xyz") -test(real_meta, fake_meta, "get_string", "test") -test(real_meta, fake_meta, "set_int", "test", 123) -test(real_meta, fake_meta, "get_int", "test") -test(real_meta, fake_meta, "set_float", "test", 123.456789) -test(real_meta, fake_meta, "get_float", "test") -test(real_meta, fake_meta, "get_keys") -test(real_meta, fake_meta, "to_table") -test(real_meta, fake_meta, "equals", real_meta) - --- Error tests -if error_tests then - for value in iter_values() do - test(real_meta, fake_meta, "from_table", value) - test(real_meta, fake_meta, "contains", value) - test(real_meta, fake_meta, "get", value) - test(real_meta, fake_meta, "set_string", value, "test") - test(real_meta, fake_meta, "set_string", "test", value) - test(real_meta, fake_meta, "get_string", value) - test(real_meta, fake_meta, "set_int", value, 0) - test(real_meta, fake_meta, "set_int", "test", value) - test(real_meta, fake_meta, "get_int", value) - test(real_meta, fake_meta, "set_float", value, 0) - test(real_meta, fake_meta, "set_float", "test", value) - test(real_meta, fake_meta, "get_float", value) - test(real_meta, fake_meta, "equals", value) - end -end - --- Reset -real_meta:from_table(old_meta) - - --- Misc tests ------------------------------- - -local basic_vector = {x = 1, y = 1, z = 1} - --- Vector type check -if not fakelib.is_vector({x = 1, y = 1, z = 1}) then - success = false - print("[fakelib] Basic vector is not a vector.") -end -if not fakelib.is_vector(vector.new(1, 1, 1)) then - success = false - print("[fakelib] Vector is not a vector.") -end -for value in iter_values() do - if fakelib.is_vector(value) then - print(string.format("[fakelib] Value '%s' is a vector.", tostring(value))) - end -end - --- Vector metatable -if fakelib.is_vector(basic_vector, true) then - if not basic_vector.add then - print("[fakelib] Failed to add vector metatable to basic vector.") - end -end - ------------------------------- - -return success diff --git a/mods/fmod/.cdb.json b/mods/fmod/.cdb.json deleted file mode 100644 index 522bceb4..00000000 --- a/mods/fmod/.cdb.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "MOD", - "name": "fmod", - "title": "fmod", - "license": "LGPL-3.0-or-later", - "media_license": "CC-BY-SA-4.0", - "repo": "https://github.com/fluxionary/minetest-fmod.git", - "website": "https://github.com/fluxionary/minetest-fmod", - "issue_tracker": "https://github.com/fluxionary/minetest-fmod/issues", - "short_description": "flux's mod boilerplate" -} diff --git a/mods/fmod/.check_date.sh b/mods/fmod/.check_date.sh deleted file mode 100755 index 831a6ddd..00000000 --- a/mods/fmod/.check_date.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -grep $(date -u -I) mod.conf -exit $? diff --git a/mods/fmod/.editorconfig b/mods/fmod/.editorconfig deleted file mode 100644 index 33568faf..00000000 --- a/mods/fmod/.editorconfig +++ /dev/null @@ -1,14 +0,0 @@ -# See https://editorconfig.org/ - -root = true - -[*] -charset = utf-8 -end_of_line = lf -indent_style = space -indent_size = 4 -insert_final_newline = true -trim_trailing_whitespace = true - -[*.{lua,luacheckrc}] -indent_style = tab diff --git a/mods/fmod/.github/FUNDING.yml b/mods/fmod/.github/FUNDING.yml deleted file mode 100644 index c39ac9d9..00000000 --- a/mods/fmod/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: fluxionary diff --git a/mods/fmod/.github/workflows/pre-commit.yml b/mods/fmod/.github/workflows/pre-commit.yml deleted file mode 100644 index d1faea17..00000000 --- a/mods/fmod/.github/workflows/pre-commit.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: pre-commit -on: [push, pull_request, workflow_dispatch] - -jobs: - check: - runs-on: ubuntu-latest - - steps: - - name: update - run: sudo apt-get update -y - - - uses: actions/checkout@master - - uses: actions/setup-python@master - - - name: install luarocks - run: sudo apt-get install -y luarocks - - - name: add luarocks path - run: echo "$HOME/.luarocks/bin" >> $GITHUB_PATH - - - name: luacheck install - run: luarocks install --local luacheck - - - name: install cargo - run: sudo apt-get install -y cargo - - - name: install stylua - run: cargo install stylua - - - name: Install pre-commit - run: pip3 install pre-commit - - - name: Run pre-commit - run: pre-commit run --all-files diff --git a/mods/fmod/.pre-commit-config.yaml b/mods/fmod/.pre-commit-config.yaml deleted file mode 100644 index 6d4489f9..00000000 --- a/mods/fmod/.pre-commit-config.yaml +++ /dev/null @@ -1,41 +0,0 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.3.0 - hooks: - - id: fix-byte-order-marker - - id: end-of-file-fixer - - id: trailing-whitespace - - id: mixed-line-ending - args: [ --fix=lf ] - - - repo: local - hooks: - - id: detect_debug - name: detect debug - language: pygrep - entry: DEBUG - pass_filenames: true - exclude: .pre-commit-config.yaml - fail_fast: true - - id: date_version - name: date version - language: script - entry: .check_date.sh - files: mod.conf - always_run: true - fail_fast: true - - id: stylua - name: stylua - language: system - entry: stylua - pass_filenames: true - types: [ file, lua ] - fail_fast: true - - id: luacheck - name: luacheck - language: system - entry: luacheck - pass_filenames: true - types: [ file, lua ] - args: [ -q ] - fail_fast: true diff --git a/mods/fmod/LICENSE.txt b/mods/fmod/LICENSE.txt deleted file mode 100644 index b6ff27cc..00000000 --- a/mods/fmod/LICENSE.txt +++ /dev/null @@ -1,168 +0,0 @@ -this license is for the code. -any non-code media included in this repository is covered by the contents of MEDIA_LICENSE.txt. - - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/mods/fmod/MEDIA_LICENSE.txt b/mods/fmod/MEDIA_LICENSE.txt deleted file mode 100644 index 7d4f96c5..00000000 --- a/mods/fmod/MEDIA_LICENSE.txt +++ /dev/null @@ -1,427 +0,0 @@ -Attribution-ShareAlike 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution-ShareAlike 4.0 International Public -License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution-ShareAlike 4.0 International Public License ("Public -License"). To the extent this Public License may be interpreted as a -contract, You are granted the Licensed Rights in consideration of Your -acceptance of these terms and conditions, and the Licensor grants You -such rights in consideration of benefits the Licensor receives from -making the Licensed Material available under these terms and -conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. BY-SA Compatible License means a license listed at - creativecommons.org/compatiblelicenses, approved by Creative - Commons as essentially the equivalent of this Public License. - - d. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - e. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - f. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - g. License Elements means the license attributes listed in the name - of a Creative Commons Public License. The License Elements of this - Public License are Attribution and ShareAlike. - - h. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - i. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - j. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - k. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - l. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - m. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. Additional offer from the Licensor -- Adapted Material. - Every recipient of Adapted Material from You - automatically receives an offer from the Licensor to - exercise the Licensed Rights in the Adapted Material - under the conditions of the Adapter's License You apply. - - c. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - b. ShareAlike. - - In addition to the conditions in Section 3(a), if You Share - Adapted Material You produce, the following conditions also apply. - - 1. The Adapter's License You apply must be a Creative Commons - license with the same License Elements, this version or - later, or a BY-SA Compatible License. - - 2. You must include the text of, or the URI or hyperlink to, the - Adapter's License You apply. You may satisfy this condition - in any reasonable manner based on the medium, means, and - context in which You Share Adapted Material. - - 3. You may not offer or impose any additional or different terms - or conditions on, or apply any Effective Technological - Measures to, Adapted Material that restrict exercise of the - rights granted under the Adapter's License You apply. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material, - including for purposes of Section 3(b); and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - -Creative Commons is not a party to its public -licenses. Notwithstanding, Creative Commons may elect to apply one of -its public licenses to material it publishes and in those instances -will be considered the ā€œLicensor.ā€ The text of the Creative Commons -public licenses is dedicated to the public domain under the CC0 Public -Domain Dedication. Except for the limited purpose of indicating that -material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the -public licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/mods/fmod/README.md b/mods/fmod/README.md deleted file mode 100644 index ca3fab52..00000000 --- a/mods/fmod/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# fmod - -flux's mod boilerplate - -## what? - -i use this to create a common basic API that all of my mods share. it grabs a lot of mod metadata from e.g. mod.conf -and settingtypes.txt and automatically makes those values available. - -this mod is primarily for my own use, but i'd be elated if other people find it useful. suggestions are welcome, but -note that my own patterns come first in any suggested changes. the more mods i create which depend on this (there's -over 70 and counting, though only 50 or 60 are released), the harder it will be to change fundamental features. but -if i've made major mistake somehow, please let me know sooner rather than later! - -## public API - -* `modname = fmod.create(fork)` - - creates the boilerplate. - - `fork` is an optional parameter for other people to use if they fork a mod. - -the api which is created looks like this: - -```lua -local f = string.format -local modname = minetest.get_current_modname() -local S = minetest.get_translator(modname) -local modpath = minetest.get_modpath(modname) -local mod_conf = Settings(modpath .. DIR_DELIM .. "settingtypes.txt") - -modname = { - modname = modname, - modpath = modpath, - title = mod_conf:get("title") or modname, - description = mod_conf:get("description"), - author = mod_conf:get("author"), - license = mod_conf:get("license"), - version = mod_conf:get("version"), - fork = fork or "flux", -- fork is the argument to `fmod.create(fork)` - - S = S, - - has = build_has(mod_conf), -- this reads mod.conf and creates a set from optional_depends - settings = get_settings(modname), -- this reads and parses settingtypes.txt, and populates w/ values from - -- minetest.conf, or otherwise the defaults specified in settingtypes.txt - - check_version = function(required) - assert(mod_conf:get("version") >= required, f("%s requires a newer version of %s; please update it", minetest.get_current_modname(), modname)) - end, - - check_minetest_version = function(major, minor, patch, other, reason) - assert(..., "check that the required version of the game engine is present. \"other\" is reserved for " .. - "future use, once i figure out how to deal w/ engine forks." - ) - end, - - log = function(level, messagefmt, ...) - return minetest.log(level, f("[%s] %s", modname, f(messagefmt, ...))) - end, - - chat_send_player = function(player, messagefmt, ...) - minetest.chat_send_player(player, f("[%s] %s", modname, S(messagefmt, ...))) - end, - - chat_send_all = function(message, ...) - minetest.chat_send_all(f("[%s] %s", modname, S(message, ...))) - end, - - dofile = function(...) - return dofile(table.concat({modpath, ...}, DIR_DELIM) .. ".lua") - end, - - async_dofile = function(...) -- load a file into the async environment - return minetest.register_async_dofile(table.concat({ modpath, ... }, DIR_DELIM) .. ".lua") - end, -} -``` - -note that fmod itself is created w/ this same boilerplate; therefore, i can indicate that certain mods require an -up-to-date version of fmod to operate. diff --git a/mods/fmod/build_has.lua b/mods/fmod/build_has.lua deleted file mode 100644 index 691872ee..00000000 --- a/mods/fmod/build_has.lua +++ /dev/null @@ -1,12 +0,0 @@ -return function(mod_conf) - local optional_depends = mod_conf:get("optional_depends") - if not optional_depends then - return {} - end - local has = {} - for _, mod in ipairs(optional_depends:split()) do - mod = mod:trim() - has[mod] = minetest.get_modpath(mod) and true or false - end - return has -end diff --git a/mods/fmod/get_settings.lua b/mods/fmod/get_settings.lua deleted file mode 100644 index 8a0024e6..00000000 --- a/mods/fmod/get_settings.lua +++ /dev/null @@ -1,150 +0,0 @@ --- https://github.com/minetest/minetest/blob/master/builtin/settingtypes.txt --- https://github.com/minetest/minetest/blob/master/builtin/mainmenu/settings/settingtypes.lua - -local f = string.format - -local function get_lines_from_file(filename) - local fh = io.open(filename, "r") - if not fh then - return - end - local lines = fh:read("*all"):split("\n") - fh:close() - return lines -end - -local function strip_readable_name(text) - if text:sub(1, 1) ~= "(" then - error(f("%q %s", text, text)) - end - local depth = 1 - local i = 2 - while depth > 0 do - if text:sub(i, i) == ")" then - depth = depth - 1 - elseif text:sub(i, i) == "(" then - depth = depth + 1 - end - i = i + 1 - end - return text:sub(i):trim() -end - -local function starts_with(s, start) - return s:sub(1, #start) == start -end - -local function parse_line(modname, line) - if line:match("^%s*#") or line:match("^%s*%[") or line:match("^%s*$") then - return - end - line = line:trim() - local full_name, rest = unpack(line:split("%s+", false, 1, true)) - if not (full_name and rest) then - return - end - local secure = false - if starts_with(full_name, "secure.") then - secure = true - full_name = full_name:sub(#"secure." + 1) - end - local modname2, short_name = unpack(full_name:split("[:%.]", false, 1, true)) - assert(modname2 == modname, f("invalid setting name %s", full_name)) - rest = strip_readable_name(rest) - local datatype, default, params - datatype, rest = unpack(rest:split("%s", true, 1, true)) - rest = rest or "" - if datatype == "string" then - if rest:sub(1, 1) == '"' and rest:sub(#rest, #rest) == '"' then - -- this is not actually according to spec settingtypes.txt, but there's no good way to specify that the - -- default value is a single space, so we invent our own syntax - default = rest:sub(2, #rest - 1) - elseif rest:sub(1, 2) == '\\"' then - default = rest:sub(2) - else - default = rest - end - params = "" - else - default, params = unpack(rest:split("%s+", false, 1, true)) - end - - full_name = (secure and "secure." or "") .. full_name - return full_name, short_name, datatype, default, params -end - -local getters = { - -- TODO there's other setting types, but i don't use them and no-one else uses this mod - int = function(full_name, default, params) - return tonumber(minetest.settings:get(full_name)) or tonumber(default) - end, - string = function(full_name, default, params) - return minetest.settings:get(full_name) or default - end, - bool = function(full_name, default, params) - return minetest.settings:get_bool(full_name, minetest.is_yes(default)) - end, - float = function(full_name, default, params) - return tonumber(minetest.settings:get(full_name)) or tonumber(default) - end, - enum = function(full_name, default, params) - return minetest.settings:get(full_name) or default - end, - path = function(full_name, default, params) - return minetest.settings:get(full_name) or default or "" - end, - filepath = function(full_name, default, params) - return minetest.settings:get(full_name) or default or "" - end, - key = function(full_name, default, params) - return minetest.settings:get(full_name) or default - end, - flags = function(full_name, default, params) - return (minetest.settings:get(full_name) or default):split() - end, - v3f = function(full_name, default, params) - return minetest.string_to_pos(minetest.settings:get(full_name) or default) - end, -} - -return function(modname) - local modpath = minetest.get_modpath(modname) - local settingtypes_lines = get_lines_from_file(modpath .. DIR_DELIM .. "settingtypes.txt") - - if not settingtypes_lines then - return - end - - local settings = {} - for _, line in ipairs(settingtypes_lines) do - local full_name, short_name, datatype, default, params = parse_line(modname, line) - if full_name then - local getter = getters[datatype] - if getter then - settings[short_name] = getter(full_name, default, params) - else - error("TODO: implement parsing settings of type " .. datatype) - end - end - end - - local listeners_by_key = {} - - return setmetatable({ - _subscribe_for_modification = function(self, key, func) - local listeners = listeners_by_key[key] or {} - table.insert(listeners, func) - listeners_by_key[key] = listeners - end, - }, { - __index = function(self, key) - return settings[key] - end, - __newindex = function(self, key, value) - settings[key] = value - for _, func in ipairs(listeners_by_key[key] or {}) do - func(value) - end - end, - }) -end diff --git a/mods/fmod/init.lua b/mods/fmod/init.lua deleted file mode 100644 index ba4f05aa..00000000 --- a/mods/fmod/init.lua +++ /dev/null @@ -1,169 +0,0 @@ -local f = string.format - -local get_current_modname = minetest.get_current_modname -local get_modpath = minetest.get_modpath - -local our_modname = get_current_modname() -local our_modpath = get_modpath(our_modname) - -local build_has = dofile(our_modpath .. DIR_DELIM .. "build_has.lua") -local get_settings = dofile(our_modpath .. DIR_DELIM .. "get_settings.lua") -local parse_version = dofile(our_modpath .. DIR_DELIM .. "parse_version.lua") - -local function create(fork, extra_private_state) - local modname = get_current_modname() - local modpath = get_modpath(modname) - local S = minetest.get_translator(modname) - local F = minetest.formspec_escape - - local mod_conf = Settings(modpath .. DIR_DELIM .. "mod.conf") - assert(modname == mod_conf:get("name"), "mod name mismatch") - - local version = parse_version(mod_conf:get("version")) - - local private_state = { - -- minetest.request_insecure_environment() can't be used here - -- minetest.request_http_api() can't be used here - mod_storage = INIT == "game" and minetest.get_mod_storage(), - } - - if extra_private_state then - for k, v in pairs(extra_private_state) do - private_state[k] = v - end - end - - return { - modname = modname, - modpath = modpath, - - title = mod_conf:get("title") or modname, - description = mod_conf:get("description"), - author = mod_conf:get("author"), - license = mod_conf:get("license"), - media_license = mod_conf:get("media_license"), - website = mod_conf:get("website") or mod_conf:get("url"), - version = version, - fork = fork or "flux", - - S = S, - FS = function(...) - return F(S(...)) - end, - - has = build_has(mod_conf), - settings = get_settings(modname), - - check_version = function(required, reason) - if type(required) == "table" then - required = os.time(required) - end - local calling_modname = minetest.get_current_modname() or "UNKNOWN" - if reason then - assert( - version >= required, - f( - "%s requires a newer version of %s because %q; please update it.\n" - .. "go to the main game menu, click the content tab, browse online content, and update all.\n" - .. "(have %s, require %s)", - calling_modname, - modname, - reason, - version, - required - ) - ) - else - assert( - version >= required, - f( - "%s requires a newer version of %s; please update it.\n" - .. "go to the main game menu, click the content tab, browse online content, and update all.\n" - .. "(have %s, require %s)", - calling_modname, - modname, - version, - required - ) - ) - end - end, - - check_minetest_version = function(major, minor, patch, other, reason) - local mt_version = minetest.get_version() - -- TODO: figure out how to allow various "projects" (e.g. Minetest, Multicraft) properly - -- TODO: we perhaps want to allow depending on multiple projects, so this is complicated. - local mt_major, mt_minor, mt_patch = mt_version.string:match("^(%d+)%.(%d+)%.(%d+)") - if not (mt_major and mt_minor and mt_patch) then - error( - f( - "%s is not compatible w/ minetest %s because we don't understand the version", - modname, - mt_version.string - ) - ) - end - mt_major = tonumber(mt_major) - mt_minor = tonumber(mt_minor) - mt_patch = tonumber(mt_patch) - local check = true - if mt_major < major then - check = false - elseif mt_major == major then - if mt_minor < minor then - check = false - elseif mt_minor == minor then - if mt_patch < patch then - check = false - end - end - end - if not check then - if reason then - error( - f("%s requires at least minetest %i.%i.%i because it %q", modname, major, minor, patch, reason) - ) - else - error(f("%s requires at least minetest %i.%i.%i", modname, major, minor, patch)) - end - end - end, - - log = function(level, messagefmt, ...) - return minetest.log(level, f("[%s] %s", modname, f(messagefmt, ...))) - end, - - chat_send_player = INIT == "game" and function(player, messagefmt, ...) - if player.get_player_name then - player = player:get_player_name() - end - - minetest.chat_send_player(player, f("[%s] %s", modname, S(messagefmt, ...))) - end, - - chat_send_all = INIT == "game" and function(message, ...) - minetest.chat_send_all(f("[%s] %s", modname, S(message, ...))) - end, - - dofile = function(...) - assert(modname == get_current_modname(), "attempt to call dofile from external mod") - local filename = table.concat({ modpath, ... }, DIR_DELIM) .. ".lua" - local loader = assert(loadfile(filename)) - return loader(private_state) - end, - - async_dofile = function(...) - assert(modname == get_current_modname(), "attempt to call async_dofile from external mod") - local filename = table.concat({ modpath, ... }, DIR_DELIM) .. ".lua" - return minetest.register_async_dofile(filename) - end, - }, - private_state -end - -fmod = create() -fmod.create = create - -if INIT == "game" then - fmod.async_dofile("init") -end diff --git a/mods/fmod/mod.conf b/mods/fmod/mod.conf deleted file mode 100644 index 1a33e0dd..00000000 --- a/mods/fmod/mod.conf +++ /dev/null @@ -1,10 +0,0 @@ -name = fmod -title = fmod -description = flux's mod boilerplate -website = https://content.minetest.net/packages/rheo/fmod/ -author = flux -license = LGPL-3.0-or-later -media_license = CC-BY-SA-4.0 -version = 2024-04-03 -min_minetest_version = 5.8.0 -supported_games = * diff --git a/mods/fmod/parse_version.lua b/mods/fmod/parse_version.lua deleted file mode 100644 index c1ca253f..00000000 --- a/mods/fmod/parse_version.lua +++ /dev/null @@ -1,28 +0,0 @@ -local f = string.format - -return function(version) - local y, m, d, h, mi, s - y, m, d = version:match("^(%d%d%d%d)-(%d%d)-(%d%d)$") - if y and m and d then - return os.time({ year = tonumber(y), month = tonumber(m), day = tonumber(d) }) - end - - y, m, d, s = version:match("^(%d%d%d%d)-(%d%d)-(%d%d)[%.%s](%d+)$") - if y and m and d and s then - return os.time({ year = tonumber(y), month = tonumber(m), day = tonumber(d), sec = tonumber(s) }) - end - - y, m, d, h, mi, s = version:match("^(%d%d%d%d)-(%d%d)-(%d%d)[T ](%d%d):(%d%d):(%d%d)$") - if y and m and d and h and mi and s then - return os.time({ - year = tonumber(y), - month = tonumber(m), - day = tonumber(d), - hour = tonumber(h), - min = tonumber(mi), - sec = tonumber(s), - }) - end - - error(f("can't parse version %q", version)) -end diff --git a/mods/fmod/screenshot.png b/mods/fmod/screenshot.png deleted file mode 100644 index 77fcc588..00000000 Binary files a/mods/fmod/screenshot.png and /dev/null differ diff --git a/mods/futil/.cdb.json b/mods/futil/.cdb.json deleted file mode 100644 index 37f63526..00000000 --- a/mods/futil/.cdb.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "MOD", - "name": "futil", - "title": "futil", - "short_description": "flux's utility mod", - "repo": "https://github.com/fluxionary/minetest-futil.git", - "website": "https://github.com/fluxionary/minetest-futil", - "issue_tracker": "https://github.com/fluxionary/minetest-futil/issues", - "license": "LGPL-3.0-or-later", - "media_license": "CC-BY-SA-4.0" -} diff --git a/mods/futil/.check_date.sh b/mods/futil/.check_date.sh deleted file mode 100755 index 831a6ddd..00000000 --- a/mods/futil/.check_date.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -grep $(date -u -I) mod.conf -exit $? diff --git a/mods/futil/.editorconfig b/mods/futil/.editorconfig deleted file mode 100644 index 33568faf..00000000 --- a/mods/futil/.editorconfig +++ /dev/null @@ -1,14 +0,0 @@ -# See https://editorconfig.org/ - -root = true - -[*] -charset = utf-8 -end_of_line = lf -indent_style = space -indent_size = 4 -insert_final_newline = true -trim_trailing_whitespace = true - -[*.{lua,luacheckrc}] -indent_style = tab diff --git a/mods/futil/.github/FUNDING.yml b/mods/futil/.github/FUNDING.yml deleted file mode 100644 index c39ac9d9..00000000 --- a/mods/futil/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: fluxionary diff --git a/mods/futil/.github/workflows/pre-commit.yml b/mods/futil/.github/workflows/pre-commit.yml deleted file mode 100644 index d1faea17..00000000 --- a/mods/futil/.github/workflows/pre-commit.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: pre-commit -on: [push, pull_request, workflow_dispatch] - -jobs: - check: - runs-on: ubuntu-latest - - steps: - - name: update - run: sudo apt-get update -y - - - uses: actions/checkout@master - - uses: actions/setup-python@master - - - name: install luarocks - run: sudo apt-get install -y luarocks - - - name: add luarocks path - run: echo "$HOME/.luarocks/bin" >> $GITHUB_PATH - - - name: luacheck install - run: luarocks install --local luacheck - - - name: install cargo - run: sudo apt-get install -y cargo - - - name: install stylua - run: cargo install stylua - - - name: Install pre-commit - run: pip3 install pre-commit - - - name: Run pre-commit - run: pre-commit run --all-files diff --git a/mods/futil/.gitignore b/mods/futil/.gitignore deleted file mode 100644 index 7cd56c2d..00000000 --- a/mods/futil/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.unfinished.* diff --git a/mods/futil/.pre-commit-config.yaml b/mods/futil/.pre-commit-config.yaml deleted file mode 100644 index 6d4489f9..00000000 --- a/mods/futil/.pre-commit-config.yaml +++ /dev/null @@ -1,41 +0,0 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.3.0 - hooks: - - id: fix-byte-order-marker - - id: end-of-file-fixer - - id: trailing-whitespace - - id: mixed-line-ending - args: [ --fix=lf ] - - - repo: local - hooks: - - id: detect_debug - name: detect debug - language: pygrep - entry: DEBUG - pass_filenames: true - exclude: .pre-commit-config.yaml - fail_fast: true - - id: date_version - name: date version - language: script - entry: .check_date.sh - files: mod.conf - always_run: true - fail_fast: true - - id: stylua - name: stylua - language: system - entry: stylua - pass_filenames: true - types: [ file, lua ] - fail_fast: true - - id: luacheck - name: luacheck - language: system - entry: luacheck - pass_filenames: true - types: [ file, lua ] - args: [ -q ] - fail_fast: true diff --git a/mods/futil/API.md b/mods/futil/API.md deleted file mode 100644 index dea83d01..00000000 --- a/mods/futil/API.md +++ /dev/null @@ -1,469 +0,0 @@ -# WARNING - -this is *VERY OUT OF DATE*. this is a mod for my (flux's) personal use, and maintaining documentation outside the code -isn't worth the time. if other people start using this, i'll reconsider that position. - -## classes - -* `futil.class1(super)` - a simple class w/ optional inheritance -* `futil.class(...)` - a less simple class w/ multiple inheritance and `is_a` support - -## data structures - -* `futil.Deque` - - a [deque](https://en.wikipedia.org/wiki/Double-ended_queue). supported methods: - * `Deque:size()` - * `Deque:push_front(value)` - * `Deque:push_back(value)` - * `Deque:pop_front()` - * `Deque:pop_back()` - -* `futil.PairingHeap` - - a [pairing heap](https://en.wikipedia.org/wiki/Pairing_heap). supported methods: - * `PairingHeap:size()` - * `PairingHeap:peek_max()` - * `PairingHeap:delete(value)` - * `PairingHeap:delete_max()` - * `PairingHeap:get_priority(value)` - * `PairingHeap:set_priority(value, priority)` - -* `futil.DefaultTable` - - a table in which missing keys are automatically filled in. example usage: - ```lua - local default_table = futil.DefaultTable(function(key) return {} end) - default_table.foo.bar = 100 -- foo is automatically created as a table - ``` - -## general routines - -* `futil.check_call(func)` - - wraps `func` in a pcall. if no error occurs, returns the results. otherwise, logs and returns nil. - -* `futil.memoize1(f)` - - memoize a single-argument function - -* `futil.memoize_dumpable(f)` - - memoize a function if the arguments produce a unique result when `dump()`-ed - -* `futil.memoize1_modstorage(id, func)` - - memoize a function and store the results in modstorage, so they persist between sessions. - -* `futil.truncate(s, max_length, suffix)` - - if the string is longer than max_length, truncate it and append suffix. suffix is optional, defaults to "..." - -* `futil.lc_cmp(a, b)` - - case-insensitive comparator - -* `futil.table.set_all(t1, t2)` - - sets all key/value pairs of t2 in t1 - -* `futil.table.pairs_by_value(t, sort_function)` - - iterator which returns key/value pairs, sorted by value - -* `futil.table.pairs_by_key(t, sort_function)` - - iterator which returns key/value pairs, sorted by key - -* `futil.table.size(t)` - - gets the size of a table - -* `futil.table.is_empty(t)` - - returns true if the table is empty - -* `futil.equals(a, b)` - - returns true if the tables (or other values) are equivalent. do not use w/ recursive structures. - currently does not inspect metatables. - -* `futil.table.count_elements(t)` - - given a table in which some values may repeat, returns a table mapping values to their count. - -* `futil.table.sets_intersect(set1, set2)` - - returns true if `set1` and `set2` have any keys in common. - -* `futil.table.iterate(t)` - - iterates the values of an array-like table - -* `futil.table.reversed(t)` - - returns a reversed copy of the table. - -* `futil.table.contains(t, value)` - - returns `true` if value is in table - -* `futil.table.keys(t)` - - returns a table of the keys in the given tables. - -* `futil.table.values(t)` - - returns a table of the values in the given tables. - -* `futil.table.sort_keys(t, sort_function)` - - returns a table of the sorted keys of the given table. - -* `futil.wait(n)` - - busy-waits n microseconds - -* `futil.file_exists(path)` - - returns true if the path points to a file that can be opened - -* `futil.load_file(filename)` - - returns the contents of the file if it exists, otherwise nil. - -* `futil.write_file(filename, contents)` - - writes to a file. returns true if success, false if not. - -* `futil.path_concat(...)` - - concatenates part of a file path. - -* `futil.path_split(path)` - - splits a path into parts. - -* `futil.string.truncate(s, max_length, suffix)` - - truncate a string if it is longer than max_length, adding suffix (default "..."). - -* `futil.string.lc_cmp(a, b)` - - compares the lower-case values of strings a and b. - -* `futil.seconds_to_interval(time)` - - transforms a time (in seconds) to a format like "\[\[\[\[:]:]:]:]"p - -* `futil.format_utc(timestamp)` - - formats a timestamp in UTC. - -### predicates - -* `futil.is_nil(v)` - - true if v is `nil` - -* `futil.is_boolean(v)` - - true if `v` is a boolean. - -* `futil.is_number(v)` - - true if `v` is a number. - -* `futil.is_string(v)` - - true if `v` is a string. - -* `futil.is_userdata(v)` - - true if `v` is userdata. - -* `futil.is_function(v)` - - true if `v` is a function. - -* `futil.is_thread(v)` - - true if `v` is a thread. - -* `futil.is_table(v)` - - true if `v` is a table. - -### functional - -* `futil.functional.noop()` - - the NOTHING function does nothing. - -* `futil.functional.identity(x)` - - returns x - -* `futil.functional.izip(...)` - - [zips](https://docs.python.org/3/library/functions.html#zip) iterators. - -* `futil.functional.zip(...)` - - [zips](https://docs.python.org/3/library/functions.html#zip) tables. - -* `futil.functional.imap(func, ...)` - - maps a function to a sequence of iterators. the first arg to func is the first element of each iterator, etc. - -* `futil.functional.map(func, ...)` - - maps a function to a sequence of tables. the first arg to func is the first element of each table, etc. - -* `futil.functional.apply(func, t)` - - for all keys `k`, set `t[k] = func(t[k])` - -* `futil.functional.reduce(func, t, initial)` - - applies binary function `func` to successive elements in t and a "total". supply `initial` if possibly `#t == 0`. - e.g. `local sum = function(values) return reduce(function(a, b) return a + b end, values, 0) end`. - -* `futil.functional.partial(func, ...)` - - curries `func`. `partial(func, a, b, c)(d, e, f) == func(a, b, c, d, e, f) - -* `futil.functional.compose(a, b)` - - binary operator which composes two functions. `compose(a, b)(x) == a(b(x))` - -* `futil.functional.ifilter(pred, i)` - - returns an interator which returns the values of iterator `i` which match predicate `pred` - -* `futil.functional.filter(pred, t)` - - returns an interator which returns the values of table `t` which match predicate `pred` - -* `futil.functional.iall(i)` - - given an iterator, returns true if all non-nil values of the iterator are not false. - -* `futil.functional.all(t)` - - given a table, returns true if the table doesn't contain any `false` values - -* `futil.functional.iany(i)` - - given an iterator, returns true if the iterator produces any non-false values. - -* `futil.functional.any(t)` - - given a table, returns true if it contains any non-false values. - -### iterators - -* `futil.iterators.range(...)` - - * one arg: return an iterator from 1 to x. - * two args: return an iterator from x to y - * three args: return an iterator from x to y, incrementing by z - -* `iterators.repeat_(value, times)` - - * times = nil: return `value` forever - * times = positive number: return `value` `times` times - -* `futil.iterators.chain(...)` - - given a sequence of iterators, return an iterator which will return the values from each in turn. - -* `futil.iterators.count(start, step)` - - returns an infinite iterator which counts from start by step. if step is not specified, counts by 1. - -* `futil.iterators.values(t)` - - returns an iterator of the values in the table. - -* `futil.list(iterator)` - - given an iterator, returns a table of the values of the iterator. - -* `futil.list_multiple(iterator)` - - given an iterator which returns multiple values on each step, create a table of tables of those values. - -### math - -* `futil.math.idiv(a, b)` - - returns the whole part of a division and the remainder, e.g. `math.floor(a/b), a%b`. - -* futil.math.bound(m, v, M) - - if v is less than m, return m. if v is greater than M, return M. else return v. - -* futil.math.in_bounds(m, v, M) - - return true if m <= v and v <= M - -* futil.math.is_integer(v) - - returns true if v is an integer. - -* futil.math.is_u8(i) - - returns true if i is a valid unsigned 8 bit value. - -* futil.math.is_u16(i) - - returns true if i is an unsigned 16 bit value. - -* `futil.math.sum(t, initial)` - - given a table, get the sum of the values in the table. initial is the value from which to start counting. - if initial is nil and the table is empty, will return nil. - -* `futil.math.isum(i, initial)` - - like the above, but given an iterator. - -## minetest-specific routines - -* `futil.add_groups(itemstring, new_groups)` - - `new_groups` should be a table of groups to add to the item's existing groups - -* `futil.remove_groups(itemstring, ...)` - - `...` should be a list of groups to remove from the item's existing groups - -* `futil.get_items_with_group(group)` - - returns a list of itemstrings which belong to the specified group - -* `futil.get_location_string(inv)` - - given an `InvRef`, get a location string suitable for use in formspec - -* `futil.resolve_item(item)` - - given an itemstring or `ItemStack`, follows aliases until it finds the real item. - returns an itemstring. - -* `futil.items_equals(item1, item2)` - - returns true if two itemstrings/stacks represent identical stacks. - -* `futil.get_blockpos(pos)` - - converts a position vector into a blockpos - -* `futil.get_block_bounds(blockpos)` - - gets the bound vectors of a blockpos - -* `futil.formspec_pos(pos)` - - convert a position into a string suitable for use in formspecs - -* `futil.iterate_area(minp, maxp)` - - creates an iterator for every point in the volume between minp and maxp - -* `futil.iterate_volume(pos, radius)` - - like the above, given a position and radius (Lāˆž metric) - -* `futil.serialize(x)` - - turns a simple lua data structure (e.g. a table no userdata or functions) into a string - -* `futil.deserialize(data)` - - the reverse of the above. not safe; do not use w/ untrusted data - -* `futil.strip_translation(msg)` - - strips minetest's translation escape sequences from a message - -* `futil.get_safe_short_description(item)` - - gets a short description which won't contain unmatched translation escapes - -* `futil.escape_texture(texturestring)` - - escapes a texture modifier, for use within another modifier - -* `futil.get_horizontal_speed(player)` - - get's a player's horizontal speed. - -* `futil.is_on_ground(player)` - - returns true if a player is standing on the ground. - NOTE: this is currently unfinished, and doesn't report correctly if a player is standing on things with complex - collision boxes which are rotated via `paramtype2="facedir"` or similar. - -### fake inventory -this is useful for testing multiple actions on an inventory without having to worry about changing the inventory or -reverting it. this is a better solution than a detached inventory, as actions on a detached inventory are still sent -to clients. fake inventories support all the regular methods of a -[minetest inventory object](https://github.com/minetest/minetest/blob/master/doc/lua_api.md#invref), -with some additions. - -* `futil.FakeInventory()` - - create a fake inventory. - -* `futil.FakeInventory.create_copy(inv)` - - copy all the inventory lists from inv into a new fake inventory. will also create a copy of another fake inventory. - -* `futil.FakeInventory.room_for_all(inv, listname, items)` - - create a copy of inv, then tests if all items in the list `items` can be inserted into listname. - -### globalstep - -implements common boilerplate for globalsteps which are intended to execute every so often. - -```lua -futil.register_globalstep({ - period = 1, -- the globalstep should be run every seconds - catchup = "single", -- whether to "catch up" if lag prevents the callback from running - -- if not specified, no catchup will be attempted. - -- if "single", the callback will be run at most once per server-step until we've caught up. - -- if "full", will re-run the callback within the current step until we've caught up. - func = function(dtime) end, -- code to execute -}) -``` - -### hud manager - -code to manage HUDs - -```lua -local hud = futil.define_hud("my_hud", { - period = nil, -- if a number is given, will automatically update the hud for all players every seconds. - name_field = nil, -- which hud field to use to store an identifier. this should be a field not used by the given - -- hud_elem_type. defaults to "name", which is good for most types. waypoints are an exception. - get_hud_def = function(player) return {} end, -- return the expected hud definition for the player. - -- if nil is returned, the hud will be removed. - enabled_by_default = false, -- whether the hud should be enabled by default. -}) - -local player = minetest.get_player_by_name("flux") -if hud:toggle_enabled(player) then - print("hud now enabled") -else - print("hud now disabled") -end - -print("hud is " .. (hud:is_enabled(player) and "enabled" or "disabled")) - -hud:update(player) -- calls hud.get_hud_def(player) and updates the players hud -``` diff --git a/mods/futil/LICENSE.txt b/mods/futil/LICENSE.txt deleted file mode 100644 index b6ff27cc..00000000 --- a/mods/futil/LICENSE.txt +++ /dev/null @@ -1,168 +0,0 @@ -this license is for the code. -any non-code media included in this repository is covered by the contents of MEDIA_LICENSE.txt. - - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/mods/futil/MEDIA_LICENSE.txt b/mods/futil/MEDIA_LICENSE.txt deleted file mode 100644 index 7d4f96c5..00000000 --- a/mods/futil/MEDIA_LICENSE.txt +++ /dev/null @@ -1,427 +0,0 @@ -Attribution-ShareAlike 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution-ShareAlike 4.0 International Public -License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution-ShareAlike 4.0 International Public License ("Public -License"). To the extent this Public License may be interpreted as a -contract, You are granted the Licensed Rights in consideration of Your -acceptance of these terms and conditions, and the Licensor grants You -such rights in consideration of benefits the Licensor receives from -making the Licensed Material available under these terms and -conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. BY-SA Compatible License means a license listed at - creativecommons.org/compatiblelicenses, approved by Creative - Commons as essentially the equivalent of this Public License. - - d. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - e. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - f. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - g. License Elements means the license attributes listed in the name - of a Creative Commons Public License. The License Elements of this - Public License are Attribution and ShareAlike. - - h. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - i. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - j. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - k. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - l. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - m. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. Additional offer from the Licensor -- Adapted Material. - Every recipient of Adapted Material from You - automatically receives an offer from the Licensor to - exercise the Licensed Rights in the Adapted Material - under the conditions of the Adapter's License You apply. - - c. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - b. ShareAlike. - - In addition to the conditions in Section 3(a), if You Share - Adapted Material You produce, the following conditions also apply. - - 1. The Adapter's License You apply must be a Creative Commons - license with the same License Elements, this version or - later, or a BY-SA Compatible License. - - 2. You must include the text of, or the URI or hyperlink to, the - Adapter's License You apply. You may satisfy this condition - in any reasonable manner based on the medium, means, and - context in which You Share Adapted Material. - - 3. You may not offer or impose any additional or different terms - or conditions on, or apply any Effective Technological - Measures to, Adapted Material that restrict exercise of the - rights granted under the Adapter's License You apply. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material, - including for purposes of Section 3(b); and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - -Creative Commons is not a party to its public -licenses. Notwithstanding, Creative Commons may elect to apply one of -its public licenses to material it publishes and in those instances -will be considered the ā€œLicensor.ā€ The text of the Creative Commons -public licenses is dedicated to the public domain under the CC0 Public -Domain Dedication. Except for the limited purpose of indicating that -material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the -public licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/mods/futil/README.md b/mods/futil/README.md deleted file mode 100644 index f7415259..00000000 --- a/mods/futil/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# futil - flux's utility mod - -a bunch of simple lua routines and data structures. this is a library which is used by other mods, which doesn't change -any behavior or add any features itself. it mostly provides the creator (flux) w/ a toolkit for solving common problems -without having to re-implement the same code in multiple mods. diff --git a/mods/futil/data_structures/bitarray.lua b/mods/futil/data_structures/bitarray.lua deleted file mode 100644 index 71ffa7aa..00000000 --- a/mods/futil/data_structures/bitarray.lua +++ /dev/null @@ -1,125 +0,0 @@ --- pack bits into doubles --- this is quite slow compared to how you'd do this in c or something. --- there's https://bitop.luajit.org/api.html but that seems limited to 32 bits. we can use the full 53 bit mantissa. - -local BITS_PER_NUMBER = 53 - -local f = string.format - -local m_floor = math.floor -local s_byte = string.byte -local s_char = string.char - -local BitArray = futil.class1() - -function BitArray:_init(size_or_bitmap) - if type(size_or_bitmap) == "number" then - local data = {} - self._size = size_or_bitmap - for i = 1, math.ceil(size_or_bitmap / BITS_PER_NUMBER) do - data[i] = 0 - end - self._data = data - elseif type(size_or_bitmap) == "table" then - if size_or_bitmap.is_a and size_or_bitmap:is_a(BitArray) then - self._size = size_or_bitmap._size - self._data = table.copy(size_or_bitmap._data) - end - end - if not self._data then - error("bitmap must be initialized w/ a size or another bitmap") - end -end - -function BitArray:__eq(other) - if self._size ~= other._size then - return false - end - for i = 1, self._size do - if self._data[i] ~= other._data[i] then - return false - end - end - return true -end - -local function get_bit(n, j) - n = n % (2 ^ j) - return m_floor(n / (2 ^ (j - 1))) == 1 -end - -function BitArray:get(k) - if type(k) ~= "number" then - return nil - elseif k <= 0 or k > self._size then - return nil - end - local i = math.ceil(k / BITS_PER_NUMBER) - local n = self._data[i] - local j = ((k - 1) % BITS_PER_NUMBER) + 1 - return get_bit(n, j) -end - -local function set_bit(n, j, v) - local current = get_bit(n, j) - if current == v then - return n - elseif v then - return n + (2 ^ (j - 1)) - else - return n - (2 ^ (j - 1)) - end -end - -function BitArray:set(k, v) - if type(v) == "number" then - if v < 0 or v > 1 then - error(f("invalid argument %s", v)) - end - v = v == 1 - elseif type(v) ~= "boolean" then - error(f("invalid argument of type %s", type(v))) - end - local i = math.ceil(k / BITS_PER_NUMBER) - local n = self._data[i] - local j = ((k - 1) % BITS_PER_NUMBER) + 1 - self._data[i] = set_bit(n, j, v) -end - -function BitArray:serialize() - local data = self._data - local parts = {} - for i = 1, #data do - local datum = data[i] - parts[i] = s_char(datum % 256) - .. s_char(m_floor(datum / 256) % 256) - .. s_char(m_floor(datum / (256 ^ 2)) % 256) - .. s_char(m_floor(datum / (256 ^ 3)) % 256) - .. s_char(m_floor(datum / (256 ^ 4)) % 256) - .. s_char(m_floor(datum / (256 ^ 5)) % 256) - .. s_char(m_floor(datum / (256 ^ 6)) % 256) - end - return table.concat(parts, "") -end - -function BitArray.deserialize(s) - if type(s) ~= "string" then - error(f("invalid argument of type %s", type(s))) - elseif #s % 7 ~= 0 then - error(f("invalid serialized string (wrong length)")) - end - local ba = BitArray(#s / 7) - local i = 1 - for a = 1, #s, 7 do - local bs = {} - for j = 0, 6 do - bs[j + 1] = s_byte(s:sub(a + j, a + j)) - end - ba._data[i] = bs[1] - + 256 * (bs[2] + 256 * (bs[3] + 256 * (bs[4] + 256 * (bs[5] + 256 * (bs[6] + 256 * bs[7]))))) - i = i + 1 - end - return ba -end - -futil.BitArray = BitArray diff --git a/mods/futil/data_structures/default_table.lua b/mods/futil/data_structures/default_table.lua deleted file mode 100644 index 3aa5e9f0..00000000 --- a/mods/futil/data_structures/default_table.lua +++ /dev/null @@ -1,11 +0,0 @@ --- https://www.lua.org/pil/13.4.3.html - -function futil.DefaultTable(initializer) - return setmetatable({}, { - __index = function(t, k) - local v = initializer(k) - t[k] = v - return v - end, - }) -end diff --git a/mods/futil/data_structures/deque.lua b/mods/futil/data_structures/deque.lua deleted file mode 100644 index 57c30313..00000000 --- a/mods/futil/data_structures/deque.lua +++ /dev/null @@ -1,88 +0,0 @@ --- inspired by https://www.lua.org/pil/11.4.html - -local Deque = futil.class1() - -function Deque:_init(def) - self._a = 0 - self._z = -1 - self._m = def and def.max_size -end - -function Deque:size() - return self._z - self._a + 1 -end - -function Deque:push_front(value) - local max_size = self._m - if max_size and (self._z - self._a + 1) >= max_size then - return false - end - local a = self._a - 1 - self._a = a - self[a] = value - return true, a -end - -function Deque:peek_front() - return self[self._a] -end - -function Deque:pop_front() - local a = self._a - if a > self._z then - return nil - end - local value = self[a] - self[a] = nil - self._a = a + 1 - return value -end - -function Deque:push_back(value) - local max_size = self._m - if max_size and (self._z - self._a + 1) >= max_size then - return false - end - local z = self._z + 1 - self._z = z - self[z] = value - return true, z -end - -function Deque:peek_back() - return self[self._z] -end - -function Deque:pop_back() - local z = self._z - if self._a > z then - return nil - end - local value = self[z] - self[z] = nil - self._z = z + 1 - return value -end - --- this iterator is kinda wonky, and the behavior may be changed in the future. --- unexpected behavior may result from modifying a deque *while* iterating it. --- note that you *cannot* iterate the deque directly using `pairs()` because of e.g. "_a" and "_z" -function Deque:iterate() - local i = self._a - 1 - return function() - i = i + 1 - return self[i] - end -end - -function Deque:clear() - for k in pairs(self) do - if type(k) == "number" then - self[k] = nil - end - end - self._a = 0 - self._z = -1 -end - -futil.Deque = Deque diff --git a/mods/futil/data_structures/init.lua b/mods/futil/data_structures/init.lua deleted file mode 100644 index 222dacad..00000000 --- a/mods/futil/data_structures/init.lua +++ /dev/null @@ -1,7 +0,0 @@ -futil.dofile("data_structures", "bitarray") -futil.dofile("data_structures", "default_table") -futil.dofile("data_structures", "deque") -futil.dofile("data_structures", "pairing_heap") -futil.dofile("data_structures", "point_search_tree") -futil.dofile("data_structures", "set") -futil.dofile("data_structures", "sparse_graph") -- requires default_table and set diff --git a/mods/futil/data_structures/pairing_heap.lua b/mods/futil/data_structures/pairing_heap.lua deleted file mode 100644 index 82d5947c..00000000 --- a/mods/futil/data_structures/pairing_heap.lua +++ /dev/null @@ -1,156 +0,0 @@ --- https://en.wikipedia.org/wiki/Pairing_heap --- https://www.cs.cmu.edu/~sleator/papers/pairing-heaps.pdf --- https://www.cise.ufl.edu/~sahni/dsaaj/enrich/c13/pairing.htm - -local inf = math.huge - -local function add_child(node1, node2) - node2.parent = node1 - node2.sibling = node1.child - node1.child = node2 -end - -local function meld(node1, node2) - if node1 == nil or node1.value == nil then - return node2 - elseif node2 == nil or node2.value == nil then - return node1 - elseif node1.priority > node2.priority then - add_child(node1, node2) - return node1 - else - add_child(node2, node1) - return node2 - end -end - -local function merge_pairs(node) - if node.value == nil or not node.sibling then - return - end - - local sibling = node.sibling - local siblingsibling = sibling.sibling - - node.sibling = nil - sibling.sibling = nil - - node = meld(node, sibling) - - if siblingsibling then - return meld(node, merge_pairs(siblingsibling)) - else - return node - end -end - -local function cut(node) - local parent = node.parent - - if parent.child == node then - parent.child = node.sibling - else - parent = parent.child - local sibling = parent.sibling - while sibling ~= node do - parent = sibling - sibling = parent.sibling - end - parent.sibling = node.sibling - end - - node.parent = nil - node.sibling = nil -end - -local function need_to_move(node, new_priority) - local cur_priority = node.priority - if cur_priority < new_priority then - -- priority increase, make sure we don't dominate our parent - local parent = node.parent - return (parent and new_priority > parent.priority) - elseif cur_priority > new_priority then - -- priority decrease, make sure our children don't dominate us - local child = node.child - while child and child ~= node do - if child.priority > new_priority then - return true - end - child = child.sibling - end - return false - else - return false - end -end - -local PairingHeap = futil.class1() - -function PairingHeap:_new() - self._nodes_by_value = {} - self._size = 0 -end - -function PairingHeap:size() - return self._size -end - -function PairingHeap:peek() - local hn = self._max_node - - if not hn then - error("empty") - end - - return hn.value, hn.priority -end - -function PairingHeap:remove(value) - self:set_priority(value, inf) - return self:pop() -end - -function PairingHeap:pop() - local max = self._max_node - - if not max then - error("empty") - end - - local child = max.child - if child then - self._max_node = merge_pairs(child) - end - - local value = max._value - - self._nodes_by_value[value] = nil - self._size = self._size - 1 - - return value -end - -function PairingHeap:get_priority(value) - return self._nodes_by_value[value].priority -end - -function PairingHeap:set_priority(value, priority) - local cur_node = self._nodes_by_value[value] - if cur_node then - local need_to = need_to_move(cur_node, priority) - - if need_to then - cut(cur_node) - self._max_node = meld(cur_node, self._max_node) - else - cur_node.priority = priority - end - else - local node = { value = value, priority = priority } - self._nodes_by_value[value] = node - self._max_node = meld(self._max_node, node) - self._size = self._size + 1 - end -end - -futil.PairingHeap = PairingHeap diff --git a/mods/futil/data_structures/point_search_tree.lua b/mods/futil/data_structures/point_search_tree.lua deleted file mode 100644 index 2c328115..00000000 --- a/mods/futil/data_structures/point_search_tree.lua +++ /dev/null @@ -1,268 +0,0 @@ ---[[ -a data structure which can efficiently retrieve values within specific rectangular regions of 3d space. - -the closest relevant descriptions of this problem and solution are in the following: -https://en.wikipedia.org/wiki/Min/max_kd-tree -https://medium.com/omarelgabrys-blog/geometric-applications-of-bsts-e58f0a5019f3 - -creation is O(n log n) -finding objects in a region is O(m + log n) if there's m objects in the region. - -the hope here is that this will provide a faster alternative to `minetest.get_objects_in_area()`. that currently -iterates over *all* active objects in the world, which can be slow when there's ten thousand or more of objects in the -world, and you are only interested in a few of them. in particular, the your-land server usually has 5-8 thousand -objects loaded at once, and can have hundreds of mobs calling `get_objects_in_area` every couple server steps. perftop -definitively implicated this routine as being a major source of lag. the question, now, is what to do about it. - -the current implementation doesn't allow for insertion, deletion, or changes in location. if your points move around, -you're gonna have to rebuild the whole tree from scratch, which currently limits the usefulness. - -TODO: read this and incorporate if applicable: https://arxiv.org/abs/1410.5420 - -== footnotes about the algorithms == -currently, we're hard-coding the usage of the [median of medians](https://en.wikipedia.org/wiki/Median_of_medians) -algorithm as the pivot strategy, as this resulted in unexpectedly dramatic improvements over random selection in -some informal performance tests i did. - -== footnotes about results == -currently, performance can range from taking 1/20th of the time of the engine call, to 100 times as long. this -makes me realize that this is worth pursuing, but probably this will need to be ported to c++ to consistently provide -a benefit. but, i'll absolutely need to figure out a self-balancing strategy before that'll be appropriate. -]] -local sort = table.sort - -local in_area = vector.in_area - or function(pos, pmin, pmax) - local x, y, z = pos.x, pos.y, pos.z - return pmin.x <= x and x <= pmax.x and pmin.y <= y and y <= pmax.y and pmin.z <= z and z <= pmax.z - end - -local axes = { "x", "y", "z" } -local POS = 1 -local VALUE = 2 - -local Leaf = futil.class1() - -function Leaf:_init(pos_and_value) - self[POS] = pos_and_value[POS] - self[VALUE] = pos_and_value[VALUE] -end - -local Node = futil.class1() - -function Node:_init(min, max, left, right) - self.min = min - self.max = max - self.left = left - self.right = right -end - -local PointSearchTree = futil.class1() - -futil.min_median_max = {} - -function futil.min_median_max.sort(t, indexer) - if indexer then - sort(t, function(a, b) - return indexer(a) < indexer(b) - end) - else - sort(t) - end - return t[1], math.floor(#t / 2), t[#t] -end - -function futil.min_median_max.gen_select(pivot_alg) - return function(t, indexer) - local median = futil.selection.select(t, pivot_alg, function(a, b) - return indexer(a) < indexer(b) - end) - local min = indexer(t[1]) - local max = min - for i = 2, #t do - local v = indexer(t[i]) - if v < min then - min = v - elseif v > max then - max = v - end - end - return min, median, max - end -end - -local function bisect(pos_and_values, axis_i, min_median_max) - if #pos_and_values == 1 then - return Leaf(pos_and_values[1]) - end - - local axis = axes[axis_i] - - local min, median, max = min_median_max(pos_and_values, function(i) - return i[POS][axis] - end) - - local next_axis_i = (axis_i % #axes) + 1 - return Node( - min, - max, - bisect({ unpack(pos_and_values, 1, median) }, next_axis_i, min_median_max), - bisect({ unpack(pos_and_values, median + 1) }, next_axis_i, min_median_max) - ) -end - -function PointSearchTree:_init(pos_and_values, min_median_max) - pos_and_values = pos_and_values or {} - min_median_max = min_median_max or futil.min_median_max.gen_select(futil.selection.pivot.median_of_medians) - self._len = #pos_and_values - if #pos_and_values > 0 then - self._root = bisect(pos_and_values, 1, min_median_max) - end -end - --- -DLUAJIT_ENABLE_LUA52COMPAT -function PointSearchTree:__len() - return self._len -end - -function PointSearchTree:dump() - local function getlines(node, axis_i) - local axis = axes[axis_i] - if not node then - return {} - elseif node:is_a(Leaf) then - return { minetest.pos_to_string(node[POS], 1) } - else - local lines = {} - for _, line in ipairs(getlines(node.left, (axis_i % #axes) + 1)) do - lines[#lines + 1] = string.format("%s=[%.1f,%.1f] %s", axis, node.min, node.max, line) - end - for _, line in ipairs(getlines(node.right, (axis_i % #axes) + 1)) do - lines[#lines + 1] = string.format("%s=[%.1f,%.1f] %s", axis, node.min, node.max, line) - end - return lines - end - end - - return table.concat(getlines(self._root, 1), "\n") -end - -local function make_iterator(pmin, pmax, predicate, accumulate) - local function iterate(node, axis_i) - local next_axis_i = (axis_i % 3) + 1 - local next_axis = axes[next_axis_i] - - local left = node.left - if left then - if left:is_a(Leaf) then - if predicate(left) then - accumulate(left) - end - elseif pmin[next_axis] <= left.max then - iterate(left, next_axis_i) - end - end - - local right = node.right - if right then - if right:is_a(Leaf) then - if predicate(right) then - accumulate(right) - end - elseif right.min <= pmax[next_axis] then - iterate(right, next_axis_i) - end - end - end - return iterate -end - -function PointSearchTree:iterate_values_in_area(pmin, pmax) - if not self._root then - return function() end - end - - pmin, pmax = vector.sort(pmin, pmax) - - if self._root.max < pmin.x or pmax.x < self._root.min then - return function() end - end - - return coroutine.wrap(function() - make_iterator(pmin, pmax, function(leaf) - return in_area(leaf[POS], pmin, pmax) - end, function(leaf) - coroutine.yield(leaf[POS], leaf[VALUE]) - end)(self._root, 1) - end) -end - -function PointSearchTree:get_values_in_area(pmin, pmax) - local via = {} - if not self._root then - return via - end - - pmin, pmax = vector.sort(pmin, pmax) - - if self._root.max < pmin.x or pmax.x < self._root.min then - return via - end - - make_iterator(pmin, pmax, function(leaf) - return in_area(leaf[POS], pmin, pmax) - end, function(leaf) - via[#via + 1] = leaf[VALUE] - end)(self._root, 1) - - return via -end - -function PointSearchTree:iterate_values_inside_radius(center, radius) - if not self._root then - return function() end - end - - local pmin = vector.subtract(center, radius) - local pmax = vector.add(center, radius) - - if self._root.max < pmin.x or pmax.x < self._root.min then - return function() end - end - - local v_distance = vector.distance - - return coroutine.wrap(function() - make_iterator(pmin, pmax, function(leaf) - return v_distance(center, leaf[POS]) <= radius - end, function(leaf) - coroutine.yield(leaf[POS], leaf[VALUE]) - end)(self._root, 1) - end) -end - -function PointSearchTree:get_values_inside_radius(center, radius) - local vir = {} - if not self._root then - return vir - end - - local pmin = vector.subtract(center, radius) - local pmax = vector.add(center, radius) - - if self._root.max < pmin.x or pmax.x < self._root.min then - return vir - end - - local v_distance = vector.distance - - make_iterator(pmin, pmax, function(leaf) - return v_distance(center, leaf[POS]) <= radius - end, function(leaf) - vir[#vir + 1] = leaf[VALUE] - end)(self._root, 1) - - return vir -end - -futil.PointSearchTree = PointSearchTree diff --git a/mods/futil/data_structures/set.lua b/mods/futil/data_structures/set.lua deleted file mode 100644 index 269cf606..00000000 --- a/mods/futil/data_structures/set.lua +++ /dev/null @@ -1,267 +0,0 @@ --- based more-or-less on python's set - -local f = string.format - -local Set = futil.class1() - -local function is_a_set(thing) - return type(thing) == "table" and type(thing.is_a) == "function" and thing:is_a(Set) -end - -function Set:_init(t_or_i) - self._size = 0 - self._set = {} - if t_or_i then - if type(t_or_i.is_a) == "function" then - if t_or_i:is_a(Set) then - self._set = table.copy(t_or_i._set) - self._size = t_or_i._size - else - for v in t_or_i:iterate() do - self:add(v) - end - end - elseif type(t_or_i) == "table" then - for i = 1, #t_or_i do - self:add(t_or_i[i]) - end - elseif type(t_or_i) == "function" or getmetatable(t_or_i).__call then - for v in t_or_i do - self:add(v) - end - else - error(f("unknown argument of type %s", type(t_or_i))) - end - end -end - --- turn a table like {foo=true, bar=true} into a Set -function Set.convert(t) - local set = Set() - set._set = t - set._size = futil.table.size(t) - return set -end - --- -DLUAJIT_ENABLE_LUA52COMPAT -function Set:__len() - return self._size -end - -function Set:len() - return self._size -end - -function Set:size() - return self._size -end - -function Set:is_empty() - return self._size == 0 -end - -function Set:__tostring() - local elements = {} - for element in pairs(self._set) do - elements[#elements + 1] = f("%q", element) - end - return f("Set({%s})", table.concat(elements, ", ")) -end - -function Set:__eq(other) - if not is_a_set(other) then - return false - end - for k in pairs(self._set) do - if not other._set[k] then - return false - end - end - return self._size == other._size -end - -function Set:contains(element) - return self._set[element] == true -end - -function Set:add(element) - if not self:contains(element) then - self._set[element] = true - self._size = self._size + 1 - end -end - -function Set:remove(element) - if not self:contains(element) then - error(f("set does not contain %s", element)) - end - self._set[element] = nil - self._size = self._size - 1 -end - -function Set:discard(element) - if self:contains(element) then - self._set[element] = nil - self._size = self._size - 1 - end -end - -function Set:clear() - self._set = {} - self._size = 0 -end - -function Set:iterate() - return futil.table.ikeys(self._set) -end - -function Set:intersects(other) - if not is_a_set(other) then - other = Set(other) - end - local smaller, bigger - if other:size() < self:size() then - smaller = other - bigger = self - else - smaller = self - bigger = other - end - for element in smaller:iterate() do - if bigger:contains(element) then - return true - end - end - return false -end - -function Set:isdisjoint(other) - if not is_a_set(other) then - other = Set(other) - end - local smaller, bigger - if other:size() < self:size() then - smaller = other - bigger = self - else - smaller = self - bigger = other - end - for element in smaller:iterate() do - if bigger:contains(element) then - return false - end - end - return true -end - -function Set:issubset(other) - if not is_a_set(other) then - other = Set(other) - end - if self:size() > other:size() then - return false - end - for element in self:iterate() do - if not other:contains(element) then - return false - end - end - return true -end - -function Set:__le(other) - return self:issubset(other) -end - -function Set:__lt(other) - if not is_a_set(other) then - other = Set(other) - end - if self:size() >= other:size() then - return false - end - for element in self:iterate() do - if not other:contains(element) then - return false - end - end - return true -end - -function Set:issuperset(other) - if not is_a_set(other) then - other = Set(other) - end - return other:issubset(self) -end - -function Set:update(other) - if not is_a_set(other) then - other = Set(other) - end - for element in other:iterate() do - self:add(element) - end -end - -function Set:union(other) - if not is_a_set(other) then - other = Set(other) - end - local union = Set(self) - union:update(other) - return union -end - -function Set:__add(other) - return self:union(other) -end - -function Set:intersection_update(other) - if not is_a_set(other) then - other = Set(other) - end - for element in self:iterate() do - if not other:contains(element) then - self:remove(element) - end - end -end - -function Set:intersection(other) - if not is_a_set(other) then - other = Set(other) - end - local intersection = Set() - for element in self:iterate() do - if other:contains(element) then - intersection:add(element) - end - end - return intersection -end - -function Set:difference_update(other) - if not is_a_set(other) then - other = Set(other) - end - for element in other:iterate() do - self:discard(element) - end -end - -function Set:difference(other) - if not is_a_set(other) then - other = Set(other) - end - local difference = Set(self) - difference:difference_update(other) - return difference -end - -function Set:__sub(other) - return self:difference(other) -end - -futil.Set = Set diff --git a/mods/futil/data_structures/sparse_graph.lua b/mods/futil/data_structures/sparse_graph.lua deleted file mode 100644 index b5acc20d..00000000 --- a/mods/futil/data_structures/sparse_graph.lua +++ /dev/null @@ -1,32 +0,0 @@ -local f = string.format - -local SparseGraph = futil.class1() - -function SparseGraph:_init(size) - self._size = size or 0 - self._adj_by_vertex = futil.DefaultTable(function() - return futil.Set() - end) -end - -function SparseGraph:size() - return self._size -end - -function SparseGraph:add_vertex() - self._size = self._size + 1 -end - -function SparseGraph:add_edge(a, b) - assert(1 <= a and a <= self._size, f("invalid vertex a %s", a)) - assert(1 <= b and b <= self._size, f("invalid vertex b %s", b)) - self._adj_by_vertex[a]:add(b) -end - -function SparseGraph:has_edge(a, b) - assert(1 <= a and a <= self._size, f("invalid vertex a %s", a)) - assert(1 <= b and b <= self._size, f("invalid vertex b %s", b)) - return self._adj_by_vertex[a]:contains(b) -end - -futil.SparseGraph = SparseGraph diff --git a/mods/futil/init.lua b/mods/futil/init.lua deleted file mode 100644 index 136d3552..00000000 --- a/mods/futil/init.lua +++ /dev/null @@ -1,11 +0,0 @@ -fmod.check_version({ year = 2023, month = 7, day = 14 }) -- async dofile - -futil = fmod.create() - -futil.dofile("util", "init") -futil.dofile("data_structures", "init") -- depends on util -futil.dofile("minetest", "init") -- depends on util and data_structures - -if INIT == "game" then - futil.async_dofile("init") -end diff --git a/mods/futil/minetest/box.lua b/mods/futil/minetest/box.lua deleted file mode 100644 index 472ff446..00000000 --- a/mods/futil/minetest/box.lua +++ /dev/null @@ -1,186 +0,0 @@ --- box definition below node boxes: https://github.com/minetest/minetest/blob/master/doc/lua_api.md#node-boxes - -local x1 = 1 -local y1 = 2 -local z1 = 3 -local x2 = 4 -local y2 = 5 -local z2 = 6 - -function futil.boxes_intersect(box1, box2) - return not ( - (box1[x2] < box2[x1] or box2[x2] < box1[x1]) - or (box1[y2] < box2[y1] or box2[y2] < box1[y1]) - or (box1[z2] < box2[z1] or box2[z2] < box1[z1]) - ) -end - -function futil.box_offset(box, number_or_vector) - if type(number_or_vector) == "number" then - return { - box[1] + number_or_vector, - box[2] + number_or_vector, - box[3] + number_or_vector, - box[4] + number_or_vector, - box[5] + number_or_vector, - box[6] + number_or_vector, - } - else - return { - box[1] + number_or_vector.x, - box[2] + number_or_vector.y, - box[3] + number_or_vector.z, - box[4] + number_or_vector.x, - box[5] + number_or_vector.y, - box[6] + number_or_vector.z, - } - end -end - -function futil.is_box(box) - if type(box) == "table" and #box == 6 then - for _, x in ipairs(box) do - if type(x) ~= "number" then - return false - end - end - return box[1] <= box[4] and box[2] <= box[5] and box[3] <= box[6] - end - return false -end - -function futil.is_boxes(boxes) - if type(boxes) ~= "table" or #boxes == 0 then - return false - end - - for _, box in ipairs(boxes) do - if not futil.is_box(box) then - return false - end - end - - return true -end - --- given a set of boxes, return a single box that covers all of them -function futil.cover_boxes(boxes) - if not futil.is_boxes(boxes) then - return { 0, 0, 0, 0, 0, 0 } - end - - local cover = boxes[1] - for i = 2, #boxes do - for j = 1, 3 do - cover[j] = math.min(cover[j], boxes[i][j]) - end - for j = 4, 6 do - cover[j] = math.max(cover[j], boxes[i][j]) - end - end - - return cover -end - ---[[ -for nodes: - A nodebox is defined as any of: - - { - -- A normal cube; the default in most things - type = "regular" - } - { - -- A fixed box (or boxes) (facedir param2 is used, if applicable) - type = "fixed", - fixed = box OR {box1, box2, ...} - } - { - -- A variable height box (or boxes) with the top face position defined - -- by the node parameter 'leveled = ', or if 'paramtype2 == "leveled"' - -- by param2. - -- Other faces are defined by 'fixed = {}' as with 'type = "fixed"'. - type = "leveled", - fixed = box OR {box1, box2, ...} - } - { - -- A box like the selection box for torches - -- (wallmounted param2 is used, if applicable) - type = "wallmounted", - wall_top = box, - wall_bottom = box, - wall_side = box - } - { - -- A node that has optional boxes depending on neighboring nodes' - -- presence and type. See also `connects_to`. - type = "connected", - fixed = box OR {box1, box2, ...} - connect_top = box OR {box1, box2, ...} - connect_bottom = box OR {box1, box2, ...} - connect_front = box OR {box1, box2, ...} - connect_left = box OR {box1, box2, ...} - connect_back = box OR {box1, box2, ...} - connect_right = box OR {box1, box2, ...} - -- The following `disconnected_*` boxes are the opposites of the - -- `connect_*` ones above, i.e. when a node has no suitable neighbor - -- on the respective side, the corresponding disconnected box is drawn. - disconnected_top = box OR {box1, box2, ...} - disconnected_bottom = box OR {box1, box2, ...} - disconnected_front = box OR {box1, box2, ...} - disconnected_left = box OR {box1, box2, ...} - disconnected_back = box OR {box1, box2, ...} - disconnected_right = box OR {box1, box2, ...} - disconnected = box OR {box1, box2, ...} -- when there is *no* neighbor - disconnected_sides = box OR {box1, box2, ...} -- when there are *no* - -- neighbors to the sides - } - -for objects: - collisionbox = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }, -- default - selectionbox = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, rotate = false }, - -- { xmin, ymin, zmin, xmax, ymax, zmax } in nodes from object position. - -- Collision boxes cannot rotate, setting `rotate = true` on it has no effect. - -- If not set, the selection box copies the collision box, and will also not rotate. - -- If `rotate = false`, the selection box will not rotate with the object itself, remaining fixed to the axes. - -- If `rotate = true`, it will match the object's rotation and any attachment rotations. - -- Raycasts use the selection box and object's rotation, but do *not* obey attachment rotations -]] - -futil.default_collision_box = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } - -function futil.node_collision_box_to_object_collisionbox(collision_box) - if type(collision_box) ~= "table" then - return table.copy(futil.default_collision_box) - elseif collision_box.type == "regular" then - return table.copy(futil.default_collision_box) - elseif collision_box.type == "fixed" or collision_box.type == "leveled" or collision_box.type == "connected" then - if futil.is_box(collision_box.fixed) then - return collision_box.fixed - elseif futil.is_boxes(collision_box.fixed) then - return futil.cover_boxes(collision_box.fixed) - else - return table.copy(futil.default_collision_box) - end - elseif collision_box.type == "wallmounted" then - local boxes = {} - if collision_box.wall_top then - table.insert(boxes, collision_box.wall_top) - end - if collision_box.wall_bottom then - table.insert(boxes, collision_box.wall_bottom) - end - if collision_box.wall_side then - table.insert(boxes, collision_box.wall_side) - end - return futil.cover_boxes(boxes) - else - return table.copy(futil.default_collision_box) - end -end - -function futil.node_selection_box_to_object_selectionbox(selection_box, rotate) - local selectionbox = futil.node_collision_box_to_object_collisionbox(selection_box) - selectionbox.rotate = rotate or false - return selectionbox -end diff --git a/mods/futil/minetest/dedupe.lua b/mods/futil/minetest/dedupe.lua deleted file mode 100644 index 2b59843e..00000000 --- a/mods/futil/minetest/dedupe.lua +++ /dev/null @@ -1,31 +0,0 @@ --- utilities to dedupe messages -local last_by_func = {} -function futil.dedupe(func, ...) - local cur = { ... } - if futil.equals(last_by_func[func], cur) then - return - end - last_by_func[func] = cur - return func(...) -end - -local last_by_player_name_by_func = futil.DefaultTable(function() - return {} -end) -function futil.dedupe_by_player(func, player, ...) - local cur = { ... } - local last_by_player_name = last_by_player_name_by_func[func] - local player_name - - if type(player) == "string" then - player_name = player - else - player_name = player:get_player_name() - end - - if futil.equals(last_by_player_name[player_name], cur) then - return - end - last_by_player_name[player_name] = cur - return func(player, ...) -end diff --git a/mods/futil/minetest/dump.lua b/mods/futil/minetest/dump.lua deleted file mode 100644 index 671169c0..00000000 --- a/mods/futil/minetest/dump.lua +++ /dev/null @@ -1,111 +0,0 @@ --- adapted from https://github.com/minetest/minetest/blob/master/builtin/common/misc_helpers.lua --- but tables are sorted - -local function sorter(a, b) - local ta, tb = type(a), type(b) - if ta ~= tb then - return ta < tb - end - if ta == "function" or ta == "userdata" or ta == "thread" or ta == "table" then - return tostring(a) < tostring(b) - else - return a < b - end -end - -local keywords = { - ["and"] = true, - ["break"] = true, - ["do"] = true, - ["else"] = true, - ["elseif"] = true, - ["end"] = true, - ["false"] = true, - ["for"] = true, - ["function"] = true, - ["goto"] = true, -- Lua 5.2 - ["if"] = true, - ["in"] = true, - ["local"] = true, - ["nil"] = true, - ["not"] = true, - ["or"] = true, - ["repeat"] = true, - ["return"] = true, - ["then"] = true, - ["true"] = true, - ["until"] = true, - ["while"] = true, -} - -local function is_valid_identifier(str) - if not str:find("^[a-zA-Z_][a-zA-Z0-9_]*$") or keywords[str] then - return false - end - return true -end - -local function basic_dump(o) - local tp = type(o) - if tp == "number" then - return tostring(o) - elseif tp == "string" then - return string.format("%q", o) - elseif tp == "boolean" then - return tostring(o) - elseif tp == "nil" then - return "nil" - -- Uncomment for full function dumping support. - -- Not currently enabled because bytecode isn't very human-readable and - -- dump's output is intended for humans. - --elseif tp == "function" then - -- return string.format("loadstring(%q)", string.dump(o)) - elseif tp == "userdata" then - return tostring(o) - else - return string.format("<%s>", tp) - end -end - -function futil.dump(o, indent, nested, level) - local t = type(o) - if not level and t == "userdata" then - -- when userdata (e.g. player) is passed directly, print its metatable: - return "userdata metatable: " .. futil.dump(getmetatable(o)) - end - if t ~= "table" then - return basic_dump(o) - end - - -- Contains table -> true/nil of currently nested tables - nested = nested or {} - if nested[o] then - return "" - end - nested[o] = true - indent = indent or "\t" - level = level or 1 - - local ret = {} - local dumped_indexes = {} - for i, v in ipairs(o) do - ret[#ret + 1] = futil.dump(v, indent, nested, level + 1) - dumped_indexes[i] = true - end - for k, v in futil.table.pairs_by_key(o, sorter) do - if not dumped_indexes[k] then - if type(k) ~= "string" or not is_valid_identifier(k) then - k = "[" .. futil.dump(k, indent, nested, level + 1) .. "]" - end - v = futil.dump(v, indent, nested, level + 1) - ret[#ret + 1] = k .. " = " .. v - end - end - nested[o] = nil - if indent ~= "" then - local indent_str = "\n" .. string.rep(indent, level) - local end_indent_str = "\n" .. string.rep(indent, level - 1) - return string.format("{%s%s%s}", indent_str, table.concat(ret, "," .. indent_str), end_indent_str) - end - return "{" .. table.concat(ret, ", ") .. "}" -end diff --git a/mods/futil/minetest/fake_inventory.lua b/mods/futil/minetest/fake_inventory.lua deleted file mode 100644 index 096f7124..00000000 --- a/mods/futil/minetest/fake_inventory.lua +++ /dev/null @@ -1,271 +0,0 @@ -local FakeInventory = futil.class1() - -local function copy_list(list) - local copy = {} - for i = 1, #list do - copy[i] = ItemStack(list[i]) - end - return copy -end - -function FakeInventory:_init() - self._lists = {} -end - -function FakeInventory.create_copy(inv) - local fake_inv = FakeInventory() - for listname, contents in pairs(inv:get_lists()) do - fake_inv:set_size(listname, inv:get_size(listname)) - fake_inv:set_width(listname, inv:get_width(listname)) - fake_inv:set_list(listname, contents) - end - return fake_inv -end - -function FakeInventory.room_for_all(inv, listname, items) - local fake_inv = FakeInventory.create_copy(inv) - for i = 1, #items do - local item = items[i] - local remainder = fake_inv:add_item(listname, item) - if not remainder:is_empty() then - return false - end - end - return true -end - -function FakeInventory:is_empty(listname) - local list = self._lists[listname] - if not list then - return true - end - for _, stack in ipairs(list) do - if not stack:is_empty() then - return false - end - end - return true -end - -function FakeInventory:get_size(listname) - local list = self._lists[listname] - if not list then - return 0 - end - return #list -end - -function FakeInventory:set_size(listname, size) - if size == 0 then - self._lists[listname] = nil - return - end - - local list = self._lists[listname] or {} - - while #list < size do - list[#list + 1] = ItemStack() - end - - for i = size + 1, #list do - list[i] = nil - end - - self._lists[listname] = list -end - -function FakeInventory:get_width(listname) - local list = self._lists[listname] or {} - return list.width or 0 -end - -function FakeInventory:set_width(listname, width) - local list = self._lists[listname] or {} - list.width = width - self._lists[listname] = list -end - -function FakeInventory:get_stack(listname, i) - local list = self._lists[listname] - if not list or i > #list then - return ItemStack() - end - return ItemStack(list[i]) -end - -function FakeInventory:set_stack(listname, i, stack) - local list = self._lists[listname] - if not list or i > #list then - return - end - list[i] = ItemStack(stack) -end - -function FakeInventory:get_list(listname) - local list = self._lists[listname] - if not list then - return - end - return copy_list(list) -end - -function FakeInventory:set_list(listname, list) - local ourlist = self._lists[listname] - if not ourlist then - return - end - - for i = 1, #ourlist do - ourlist[i] = ItemStack(list[i]) - end -end - -function FakeInventory:get_lists() - local lists = {} - for listname, list in pairs(self._lists) do - lists[listname] = copy_list(list) - end - return lists -end - -function FakeInventory:set_lists(lists) - for listname, list in pairs(lists) do - self:set_list(listname, list) - end -end - --- add item somewhere in list, returns leftover `ItemStack`. -function FakeInventory:add_item(listname, new_item) - local list = self._lists[listname] - new_item = ItemStack(new_item) - if new_item:is_empty() or not list or #list == 0 then - return new_item - end - - -- first try to find if it could be added to some existing items - for _, our_stack in ipairs(list) do - if not our_stack:is_empty() then - new_item = our_stack:add_item(new_item) - if new_item:is_empty() then - return new_item - end - end - end - - -- then try to add it to empty slots - for _, our_stack in ipairs(list) do - new_item = our_stack:add_item(new_item) - if new_item:is_empty() then - break - end - end - - return new_item -end - --- returns `true` if the stack of items can be fully added to the list -function FakeInventory:room_for_item(listname, stack) - local list = self._lists[listname] - if not list then - return false - end - - stack = ItemStack(stack) - local copy = copy_list(list) - for _, our_stack in ipairs(copy) do - stack = our_stack:add_item(stack) - if stack:is_empty() then - break - end - end - - return stack:is_empty() -end - --- take as many items as specified from the list, returns the items that were actually removed (as an `ItemStack`) --- note that any item metadata is ignored, so attempting to remove a specific unique item this way will likely remove --- the wrong one -- to do that use `set_stack` with an empty `ItemStack`. -function FakeInventory:remove_item(listname, stack) - local removed = ItemStack() - stack = ItemStack(stack) - - local list = self._lists[listname] - if not list or stack:is_empty() then - return removed - end - - local name = stack:get_name() - local count_remaining = stack:get_count() - local taken = 0 - - for i = #list, 1, -1 do - local our_stack = list[i] - if our_stack:get_name() == name then - local n = our_stack:take_item(count_remaining):get_count() - count_remaining = count_remaining - n - taken = taken + n - end - - if count_remaining == 0 then - break - end - end - - stack:set_count(taken) - - return stack -end - --- returns `true` if the stack of items can be fully taken from the list. --- If `match_meta` is false, only the items' names are compared (default: `false`). -function FakeInventory:contains_item(listname, stack, match_meta) - local list = self._lists[listname] - if not list then - return false - end - - stack = ItemStack(stack) - - if match_meta then - local name = stack:get_name() - local wear = stack:get_wear() - local meta = stack:get_meta() - local needed_count = stack:get_count() - - for _, our_stack in ipairs(list) do - if our_stack:get_name() == name and our_stack:get_wear() == wear and our_stack:get_meta():equals(meta) then - local n = our_stack:peek_item(needed_count):get_count() - needed_count = needed_count - n - end - if needed_count == 0 then - break - end - end - - return needed_count == 0 - else - local name = stack:get_name() - local needed_count = stack:get_count() - - for _, our_stack in ipairs(list) do - if our_stack:get_name() == name then - local n = our_stack:peek_item(needed_count):get_count() - needed_count = needed_count - n - end - if needed_count == 0 then - break - end - end - - return needed_count == 0 - end -end - -function FakeInventory:get_location() - return { - type = "undefined", - subtype = "FakeInventory", - } -end - -futil.FakeInventory = FakeInventory diff --git a/mods/futil/minetest/globalstep.lua b/mods/futil/minetest/globalstep.lua deleted file mode 100644 index b9c4d219..00000000 --- a/mods/futil/minetest/globalstep.lua +++ /dev/null @@ -1,88 +0,0 @@ ---[[ -execute the globalstep after the specified period. the actual amount of time elapsed is passed to the function, -and will always be greater than or equal to the length of the period. -futil.register_globalstep({ - period = 1, - func = function(elapsed) end, -}) - -execute the globalstep after the specified period. if more time has elapsed than the period specified, the remainder -will be counted against the next cycle, allowing the execution to "catch up". the expected time between executions -will tend towards the specified period. IMPORTANT: do not specify a period which is less than the length of the -dedicated server step. -futil.register_globalstep({ - period = 1, - catchup = "single" - func = function(period) end, -}) - -execute the globalstep after the specified period. if more time has elapsed than the period specified, the callback -will be executed repeatedly until the elapsed time is less than the period, and the remainder will still be counted -against the next cycle. -futil.register_globalstep({ - period = 1, - catchup = "full" - func = function(period) end, -}) - -this is just a light wrapper over a normal minetest globalstep callback, and is only provided for completeness. -futil.register_globalstep({ - func = function(dtime) end, -}) -]] -local f = string.format - -local dedicated_server_step = tonumber(minetest.settings:get("dedicated_server_step")) or 0.09 - -function futil.register_globalstep(def) - if def.period then - local elapsed = 0 - if def.catchup == "full" then - assert(def.period > 0, "full catchup will cause an infinite loop if period is 0") - minetest.register_globalstep(function(dtime) - elapsed = elapsed + dtime - if elapsed < def.period then - return - end - elapsed = elapsed - def.period - def.func(def.period) - while elapsed > def.period do - elapsed = elapsed - def.period - def.func(def.period) - end - end) - elseif def.catchup == "single" or def.catchup == true then - assert( - def.period >= dedicated_server_step, - f( - "if period (%s) is less than dedicated_server_step (%s), single catchup will never fully catch up.", - def.period, - dedicated_server_step - ) - ) - minetest.register_globalstep(function(dtime) - elapsed = elapsed + dtime - if elapsed < def.period then - return - end - elapsed = elapsed - def.period - def.func(def.period) - end) - else - -- no catchup, just reset - minetest.register_globalstep(function(dtime) - elapsed = elapsed + dtime - if elapsed < def.period then - return - end - def.func(elapsed) - elapsed = 0 - end) - end - else - -- we do nothing useful - minetest.register_globalstep(function(dtime) - def.func(dtime) - end) - end -end diff --git a/mods/futil/minetest/group.lua b/mods/futil/minetest/group.lua deleted file mode 100644 index 76ebf8a4..00000000 --- a/mods/futil/minetest/group.lua +++ /dev/null @@ -1,61 +0,0 @@ -function futil.add_groups(itemstring, new_groups) - local def = minetest.registered_items[itemstring] - if not def then - error(("attempting to override unknown item %s"):format(itemstring)) - end - local groups = table.copy(def.groups or {}) - futil.table.set_all(groups, new_groups) - minetest.override_item(itemstring, { groups = groups }) -end - -function futil.remove_groups(itemstring, ...) - local def = minetest.registered_items[itemstring] - if not def then - error(("attempting to override unknown item %s"):format(itemstring)) - end - local groups = table.copy(def.groups or {}) - for _, group in ipairs({ ... }) do - groups[group] = nil - end - minetest.override_item(itemstring, { groups = groups }) -end - -function futil.get_items_with_group(group) - if futil.items_by_group then - return futil.items_by_group[group] or {} - end - - local items = {} - - for item in pairs(minetest.registered_items) do - if minetest.get_item_group(item, group) > 0 then - table.insert(items, item) - end - end - - return items -end - -function futil.get_item_with_group(group) - return futil.get_items_with_group(group)[1] -end - -function futil.generate_items_by_group() - local items_by_group = {} - - for item, def in pairs(minetest.registered_items) do - for group in pairs(def.groups or {}) do - local items = items_by_group[group] or {} - table.insert(items, item) - items_by_group[group] = items - end - end - - futil.items_by_group = items_by_group -end - -if INIT == "game" then - -- it's not 100% safe to assume items and groups can't change after this point. - -- but please, don't do that :\ - minetest.register_on_mods_loaded(futil.generate_items_by_group) -end diff --git a/mods/futil/minetest/hud_ephemeral.lua b/mods/futil/minetest/hud_ephemeral.lua deleted file mode 100644 index c19ecea8..00000000 --- a/mods/futil/minetest/hud_ephemeral.lua +++ /dev/null @@ -1,100 +0,0 @@ -local f = string.format - -local current_id = 0 - -local function get_next_id() - current_id = current_id + 1 - return current_id -end - -local EphemeralHud = futil.class1() - -function EphemeralHud:_init(player, hud_def) - self._player_name = player:get_player_name() - if (hud_def.type or hud_def.hud_elem_type) == "waypoint" then - self._id_field = "text2" - else - self._id_field = "name" - end - self._id = f("ephemeral_hud:%s:%i", hud_def[self._id_field] or "", get_next_id()) - hud_def[self._id_field] = self._id - self._hud_id = player:hud_add(hud_def) -end - -function EphemeralHud:is_active() - if not self._hud_id then - return false - end - local player = minetest.get_player_by_name(self._player_name) - if not player then - self._hud_id = nil - return false - end - local hud_def = player:hud_get(self._hud_id) - if not hud_def then - self._hud_id = nil - return false - end - if hud_def[self._id_field] ~= self._id then - self._hud_id = nil - return false - end - - return true -end - -function EphemeralHud:change(new_hud_def) - if not self:is_active() then - futil.log("warning", "[ephemeral hud] cannot update an inactive hud") - return false - end - local player = minetest.get_player_by_name(self._player_name) - local old_hud_def = player:hud_get(self._hud_id) - for key, value in pairs(new_hud_def) do - if key == "hud_elem_type" then - if value ~= (old_hud_def.type or old_hud_def.hud_elem_type) then - error("cannot change hud_elem_type") - end - elseif key == "type" then - if value ~= (old_hud_def.type or old_hud_def.hud_elem_type) then - error("cannot change type") - end - elseif key == self._id_field then - if value ~= self._id then - error(f("cannot change the value of %q, as this is an ID", self._id_field)) - end - else - if key == "position" or key == "scale" or key == "align" or key == "offset" then - value = futil.vector.v2f_to_float_32(value) - end - - if not futil.equals(old_hud_def[key], value) then - player:hud_change(self._hud_id, key, value) - end - end - end - return true -end - -function EphemeralHud:remove() - if not self:is_active() then - futil.log("warning", "[ephemeral hud] cannot remove an inactive hud") - return false - end - local player = minetest.get_player_by_name(self._player_name) - player:hud_remove(self._hud_id) - self._hud_id = nil -end - -futil.EphemeralHud = EphemeralHud - --- note: sometimes HUDs can fail to get created. if so, the HUD object returned here will be "inactive". -function futil.create_ephemeral_hud(player, timeout, hud_def) - local hud = EphemeralHud(player, hud_def) - minetest.after(timeout, function() - if hud:is_active() then - hud:remove() - end - end) - return hud -end diff --git a/mods/futil/minetest/hud_manager.lua b/mods/futil/minetest/hud_manager.lua deleted file mode 100644 index 04d9ab06..00000000 --- a/mods/futil/minetest/hud_manager.lua +++ /dev/null @@ -1,172 +0,0 @@ ---[[ -local my_hud = futil.define_hud("my_mod:my_hud", { - period = 1, - catchup = nil, -- not currently supported - name_field = nil, -- in case you want to override the id field - enabled_by_default = nil, -- set to true to enable by default - get_hud_data = function() - -- get data that's identical for all players - -- passed to get_hud_def - end, - get_hud_def = function(player, data) - return {} - end, -}) - -my_hud:toggle_enabled(player) -]] - -local f = string.format - -local ManagedHud = futil.class1() - -function ManagedHud:_init(hud_name, def) - self.name = hud_name - - self._name_field = def.name_field or ((def.type or def.hud_elem_type) == "waypoint" and "text2" or "name") - self._period = def.period - self._get_hud_data = def.get_hud_data - self._get_hud_def = def.get_hud_def - self._enabled_by_default = def.enabled_by_default - - self._hud_id_by_player_name = {} - - self._hud_enabled_key = f("hud_manager:%s_enabled", hud_name) - self._hud_name = f("hud_manager:%s", hud_name) -end - -function ManagedHud:is_enabled(player) - local meta = player:get_meta() - local value = meta:get(self._hud_enabled_key) - if value == nil then - return self._enabled_by_default - else - return minetest.is_yes(value) - end -end - -function ManagedHud:set_enabled(player, value) - local meta = player:get_meta() - if minetest.is_yes(value) then - meta:set_string(self._hud_enabled_key, "y") - else - meta:set_string(self._hud_enabled_key, "n") - end -end - -function ManagedHud:toggle_enabled(player) - local meta = player:get_meta() - local enabled = not self:is_enabled(player) - if enabled then - meta:set_string(self._hud_enabled_key, "y") - else - meta:set_string(self._hud_enabled_key, "n") - end - return enabled -end - -function ManagedHud:update(player, data) - local is_enabled = self:is_enabled(player) - local player_name = player:get_player_name() - local hud_id = self._hud_id_by_player_name[player_name] - local old_hud_def - if hud_id then - old_hud_def = player:hud_get(hud_id) - if old_hud_def and old_hud_def[self._name_field] == self._hud_name then - if not is_enabled then - player:hud_remove(hud_id) - self._hud_id_by_player_name[player_name] = nil - return - end - else - -- hud_id is bad - hud_id = nil - old_hud_def = nil - end - end - - if is_enabled then - local new_hud_def = self._get_hud_def(player, data) - if not new_hud_def then - if hud_id then - player:hud_remove(hud_id) - self._hud_id_by_player_name[player_name] = nil - end - return - elseif new_hud_def[self._name_field] and new_hud_def[self._name_field] ~= self._hud_name then - error(f("you cannot specify the value of the %q field, this is generated", self._name_field)) - end - - if old_hud_def then - for k, v in pairs(new_hud_def) do - if k == "position" or k == "scale" or k == "align" or k == "offset" then - v = futil.vector.v2f_to_float_32(v) - end - - if not futil.equals(old_hud_def[k], v) and k ~= "type" and k ~= "hud_elem_type" then - player:hud_change(hud_id, k, v) - end - end - else - new_hud_def[self._name_field] = self._hud_name - hud_id = player:hud_add(new_hud_def) - end - end - - self._hud_id_by_player_name[player_name] = hud_id -end - -futil.defined_huds = {} - -function futil.define_hud(hud_name, def) - if futil.defined_huds[hud_name] then - error(f("hud %s already exists", hud_name)) - end - local hud = ManagedHud(hud_name, def) - futil.defined_huds[hud_name] = hud - return hud -end - --- TODO: register_hud instead of define_hud, plus alias the old - -local function update_hud(hud, players) - local data - if hud._get_hud_data then - local is_any_enabled = false - for i = 1, #players do - if hud:is_enabled(players[i]) then - is_any_enabled = true - break - end - end - if is_any_enabled then - data = hud._get_hud_data() - end - end - for i = 1, #players do - hud:update(players[i], data) - end -end - --- TODO refactor to use futil.register_globalstep for each hud, to allow use of catchup mechanics --- ... why would HUD updates need catchup mechanics? -local elapsed_by_hud_name = {} -minetest.register_globalstep(function(dtime) - local players = minetest.get_connected_players() - if #players == 0 then - return - end - for hud_name, hud in pairs(futil.defined_huds) do - if hud._period then - local elapsed = (elapsed_by_hud_name[hud_name] or 0) + dtime - if elapsed < hud._period then - elapsed_by_hud_name[hud_name] = elapsed - else - elapsed_by_hud_name[hud_name] = 0 - update_hud(hud, players) - end - else - update_hud(hud, players) - end - end -end) diff --git a/mods/futil/minetest/image.lua b/mods/futil/minetest/image.lua deleted file mode 100644 index 65ced329..00000000 --- a/mods/futil/minetest/image.lua +++ /dev/null @@ -1,171 +0,0 @@ -local f = string.format - -local function is_vertical_frames(animation) - return (animation.type == "vertical_frames" and animation.aspect_w and animation.aspect_h) -end - -local function get_single_frame(animation, image_name) - return ("[combine:%ix%i^[noalpha^[colorize:#FFF:255^[mask:%s"):format( - animation.aspect_w, - animation.aspect_h, - image_name - ) -end - -local function is_sheet_2d(animation) - return (animation.type == "sheet_2d" and animation.frames_w and animation.frames_h) -end - -local function get_sheet_2d(animation, image_name) - return ("%s^[sheet:%ix%i:0,0"):format(image_name, animation.frames_w, animation.frames_h) -end - -local get_image_from_tile = futil.memoize1(function(tile) - if type(tile) == "string" then - return tile - elseif type(tile) == "table" then - local image_name - - if type(tile.image) == "string" then - image_name = tile.image - elseif type(tile.name) == "string" then - image_name = tile.name - end - - if image_name then - local animation = tile.animation - if animation then - if is_vertical_frames(animation) then - return get_single_frame(animation, image_name) - elseif is_sheet_2d(animation) then - return get_sheet_2d(animation, image_name) - end - end - - return image_name - end - end - - return "unknown_node.png" -end) - -local function get_image_cube(tiles) - if #tiles >= 6 then - return minetest.inventorycube( - get_image_from_tile(tiles[1] or "no_texture.png"), - get_image_from_tile(tiles[6] or "no_texture.png"), - get_image_from_tile(tiles[3] or "no_texture.png") - ) - elseif #tiles == 5 then - return minetest.inventorycube( - get_image_from_tile(tiles[1] or "no_texture.png"), - get_image_from_tile(tiles[5] or "no_texture.png"), - get_image_from_tile(tiles[3] or "no_texture.png") - ) - elseif #tiles == 4 then - return minetest.inventorycube( - get_image_from_tile(tiles[1] or "no_texture.png"), - get_image_from_tile(tiles[4] or "no_texture.png"), - get_image_from_tile(tiles[3] or "no_texture.png") - ) - elseif #tiles == 3 then - return minetest.inventorycube( - get_image_from_tile(tiles[1] or "no_texture.png"), - get_image_from_tile(tiles[3] or "no_texture.png"), - get_image_from_tile(tiles[3] or "no_texture.png") - ) - elseif #tiles == 2 then - return minetest.inventorycube( - get_image_from_tile(tiles[1] or "no_texture.png"), - get_image_from_tile(tiles[2] or "no_texture.png"), - get_image_from_tile(tiles[2] or "no_texture.png") - ) - elseif #tiles == 1 then - return minetest.inventorycube( - get_image_from_tile(tiles[1] or "no_texture.png"), - get_image_from_tile(tiles[1] or "no_texture.png"), - get_image_from_tile(tiles[1] or "no_texture.png") - ) - end - - return "no_texture.png" -end - -local function is_normal_node(drawtype) - return ( - drawtype == "normal" - or drawtype == "allfaces" - or drawtype == "allfaces_optional" - or drawtype == "glasslike" - or drawtype == "glasslike_framed" - or drawtype == "glasslike_framed_optional" - or drawtype == "liquid" - ) -end - -local cache = {} - -function futil.get_wield_image(item) - if type(item) == "string" then - item = ItemStack(item) - end - - if item:is_empty() then - return "blank.png" - end - - local def = item:get_definition() - if not def then - return "unknown_item.png" - end - - local itemstring = item:to_string() - local cached = cache[itemstring] - if cached then - return cached - end - - local meta = item:get_meta() - local color = meta:get("color") or def.color - - local image = "no_texture.png" - - if def.wield_image and def.wield_image ~= "" then - local parts = { def.wield_image } - if color then - parts[#parts + 1] = f("[colorize:%s:alpha", futil.escape_texture(color)) - end - if def.wield_overlay then - parts[#parts + 1] = def.wield_overlay - end - image = table.concat(parts, "^") - elseif def.inventory_image and def.inventory_image ~= "" then - local parts = { def.inventory_image } - if color then - parts[#parts + 1] = f("[colorize:%s:alpha", futil.escape_texture(color)) - end - if def.inventory_overlay then - parts[#parts + 1] = def.inventory_overlay - end - image = table.concat(parts, "^") - elseif def.type == "node" then - if def.drawtype == "nodebox" or def.drawtype == "mesh" then - image = "no_texture.png" - else - local tiles = def.tiles - if type(tiles) == "string" then - image = get_image_from_tile(tiles) - elseif type(tiles) == "table" then - if is_normal_node(def.drawtype) then - image = get_image_cube(tiles) - else - image = get_image_from_tile(tiles[1]) - end - end - end - end - - cache[itemstring] = image - - return image -end diff --git a/mods/futil/minetest/init.lua b/mods/futil/minetest/init.lua deleted file mode 100644 index 24dd60a2..00000000 --- a/mods/futil/minetest/init.lua +++ /dev/null @@ -1,23 +0,0 @@ -futil.dofile("minetest", "box") -futil.dofile("minetest", "dedupe") -futil.dofile("minetest", "dump") -futil.dofile("minetest", "fake_inventory") -futil.dofile("minetest", "group") -futil.dofile("minetest", "image") -futil.dofile("minetest", "item") -futil.dofile("minetest", "registration") -futil.dofile("minetest", "serialization") -futil.dofile("minetest", "strip_translation") -futil.dofile("minetest", "texture") -futil.dofile("minetest", "time") -futil.dofile("minetest", "vector") - -if INIT == "game" then - futil.dofile("minetest", "globalstep") - futil.dofile("minetest", "hud_ephemeral") - futil.dofile("minetest", "hud_manager") - futil.dofile("minetest", "inventory") - futil.dofile("minetest", "object") - futil.dofile("minetest", "object_properties") - futil.dofile("minetest", "raycast") -end diff --git a/mods/futil/minetest/inventory.lua b/mods/futil/minetest/inventory.lua deleted file mode 100644 index 46781d9b..00000000 --- a/mods/futil/minetest/inventory.lua +++ /dev/null @@ -1,40 +0,0 @@ -function futil.get_location_string(inv) - local location = inv:get_location() - if location.type == "node" then - return ("nodemeta:%i,%i,%i"):format(location.pos.x, location.pos.y, location.pos.z) - elseif location.type == "player" then - return ("player:%s"):format(location.name) - elseif location.type == "detached" then - return ("detached:%s"):format(location.name) - else - error(("unexpected location? %s"):format(dump(location))) - end -end - --- InvRef:remove_item() ignores metadata, and sometimes that's wrong --- for logic, see InventoryList::removeItem in inventory.cpp -function futil.remove_item_with_meta(inv, listname, itemstack) - itemstack = ItemStack(itemstack) - if itemstack:is_empty() then - return ItemStack() - end - local removed = ItemStack() - for i = 1, inv:get_size(listname) do - local invstack = inv:get_stack(listname, i) - if - invstack:get_name() == itemstack:get_name() - and invstack:get_wear() == itemstack:get_wear() - and invstack:get_meta() == itemstack:get_meta() - then - local still_to_remove = itemstack:get_count() - removed:get_count() - local leftover = removed:add_item(invstack:take_item(still_to_remove)) - -- if we've requested to remove more than the stack size, ignore the limit - removed:set_count(removed:get_count() + leftover:get_count()) - inv:set_stack(listname, i, invstack) - if removed:get_count() == itemstack:get_count() then - break - end - end - end - return removed -end diff --git a/mods/futil/minetest/item.lua b/mods/futil/minetest/item.lua deleted file mode 100644 index 81a6b222..00000000 --- a/mods/futil/minetest/item.lua +++ /dev/null @@ -1,133 +0,0 @@ -local f = string.format - --- if allow_unregistered is false or absent, if the original item or its alias is not a registered item, will return nil -function futil.resolve_item(item_or_string, allow_unregistered) - local item_stack = ItemStack(item_or_string) - local name = item_stack:get_name() - - local seen = { [name] = true } - - local alias = minetest.registered_aliases[name] - while alias do - name = alias - seen[name] = true - alias = minetest.registered_aliases[name] - if seen[alias] then - error(f("alias cycle on %s", name)) - end - end - - if minetest.registered_items[name] or allow_unregistered then - item_stack:set_name(name) - return item_stack:to_string() - end -end - -function futil.resolve_itemstack(item_or_string) - return ItemStack(futil.resolve_item(item_or_string, true)) -end - -if ItemStack().equals then - -- https://github.com/minetest/minetest/pull/12771 - function futil.items_equals(item1, item2) - item1 = type(item1) == "userdata" and item1 or ItemStack(item1) - item2 = type(item2) == "userdata" and item2 or ItemStack(item2) - - return item1 == item2 - end -else - local equals = futil.equals - - function futil.items_equals(item1, item2) - item1 = type(item1) == "userdata" and item1 or ItemStack(item1) - item2 = type(item2) == "userdata" and item2 or ItemStack(item2) - - return equals(item1:to_table(), item2:to_table()) - end -end - --- TODO: probably this should have a 3nd argument to handle tool and tool_group stuff -function futil.get_primary_drop(stack, filter) - stack = ItemStack(stack) - - local name = stack:get_name() - local meta = stack:get_meta() - local palette_index = tonumber(meta:get_int("palette_index")) - local def = stack:get_definition() - - if palette_index then - -- https://github.com/mt-mods/unifieddyes/blob/36c8bb5f5b8a0485225d2547c8978291ff710291/api.lua#L70-L90 - local del_color - - if def.paramtype2 == "color" and palette_index == 240 and def.palette == "unifieddyes_palette_extended.png" then - del_color = true - elseif - def.paramtype2 == "colorwallmounted" - and palette_index == 0 - and def.palette == "unifieddyes_palette_colorwallmounted.png" - then - del_color = true - elseif - def.paramtype2 == "colorfacedir" - and palette_index == 0 - and string.find(def.palette, "unifieddyes_palette_") - then - del_color = true - end - - if del_color then - meta:set_string("palette_index", "") - palette_index = nil - end - end - - local drop = def.drop - - if drop == nil then - stack:set_count(1) - return stack - elseif drop == "" then - return nil - elseif type(drop) == "string" then - drop = ItemStack(drop) - drop:set_count(1) - return drop - elseif type(drop) == "table" then - local most_common_item - local inherit_color = false - local rarity = math.huge - - if not drop.items then - error(f("unexpected drop table for %s: %s", stack:to_string(), dump(drop))) - end - - for _, items in ipairs(drop.items) do - if (items.rarity or 1) < rarity then - for item in ipairs(items.items) do - if (not filter) or filter(item) then - most_common_item = item - inherit_color = items.inherit_color or false - rarity = items.rarity - break - end - end - end - end - - if not most_common_item then - return - end - - most_common_item = ItemStack(most_common_item) - most_common_item:set_count(1) - - if inherit_color and palette_index then - local meta2 = most_common_item:get_meta() - meta2:set_int("palette_index", palette_index) - end - - return most_common_item - else - error(f("invalid drop of %s? %q", dump(name, drop))) - end -end diff --git a/mods/futil/minetest/object.lua b/mods/futil/minetest/object.lua deleted file mode 100644 index b05ee555..00000000 --- a/mods/futil/minetest/object.lua +++ /dev/null @@ -1,200 +0,0 @@ -local v_new = vector.new - --- if object is attached, get the velocity of the object it is attached to -function futil.get_velocity(object) - local parent = object:get_attach() - while parent do - object = parent - parent = object:get_attach() - end - return object:get_velocity() -end - -function futil.get_horizontal_speed(object) - local velocity = futil.get_velocity(object) - velocity.y = 0 - return vector.length(velocity) -end - -local function insert_connected(boxes, something) - if futil.is_box(something) then - table.insert(boxes, something) - elseif futil.is_boxes(something) then - table.insert_all(boxes, something) - end -end - -local function get_boxes(cb) - if not cb then - return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } } - end - - if cb.type == "regular" then - return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } } - elseif cb.type == "fixed" then - if futil.is_box(cb.fixed) then - return { cb.fixed } - elseif futil.is_boxes(cb.fixed) then - return cb.fixed - else - return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } } - end - elseif cb.type == "leveled" then - -- TODO: have to check param2 - if futil.is_box(cb.fixed) then - return { cb.fixed } - elseif futil.is_boxes(cb.fixed) then - return cb.fixed - else - return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } } - end - elseif cb.type == "wallmounted" then - -- TODO: have to check param2? or? - local boxes = {} - - if futil.is_box(cb.wall_top) then - table.insert(boxes, cb.wall_top) - end - if futil.is_box(cb.wall_bottom) then - table.insert(boxes, cb.wall_bottom) - end - if futil.is_box(cb.wall_side) then - table.insert(boxes, cb.wall_side) - end - - if #boxes > 0 then - return boxes - else - return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } } - end - elseif cb.type == "connected" then - -- TODO: very very complicated to check, just fudge and add everything - local boxes = {} - - insert_connected(boxes, cb.fixed) - insert_connected(boxes, cb.connect_top) - insert_connected(boxes, cb.connect_bottom) - insert_connected(boxes, cb.connect_front) - insert_connected(boxes, cb.connect_left) - insert_connected(boxes, cb.connect_back) - insert_connected(boxes, cb.connect_right) - insert_connected(boxes, cb.disconnected_top) - insert_connected(boxes, cb.disconnected_bottom) - insert_connected(boxes, cb.disconnected_front) - insert_connected(boxes, cb.disconnected_left) - insert_connected(boxes, cb.disconnected_back) - insert_connected(boxes, cb.disconnected_right) - insert_connected(boxes, cb.disconnected) - insert_connected(boxes, cb.disconnected_sides) - - if #boxes > 0 then - return boxes - else - return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } } - end - end - - return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } } -end - -local function get_collision_boxes(node) - local node_def = minetest.registered_nodes[node.name] - - if not node_def then - -- unknown nodes are regular solid nodes - return { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } } - end - - if not node_def.walkable then - return {} - end - - local boxes - if node_def.collision_box then - boxes = get_boxes(node_def.collision_box) - elseif node_def.drawtype == "nodebox" then - boxes = get_boxes(node_def.node_box) - else - boxes = { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 } } - end - - --[[ - if node_def.paramtype2 == "facedir" then - -- TODO: re-orient boxes - end - ]] - - return boxes -end - -local function is_pos_on_ground(feet_pos, player_box) - local node = minetest.get_node(feet_pos) - local node_boxes = get_collision_boxes(node) - - for _, node_box in ipairs(node_boxes) do - local actual_node_box = futil.box_offset(node_box, feet_pos) - if futil.boxes_intersect(actual_node_box, player_box) then - return true - end - end - - return false -end - -function futil.is_on_ground(player) - local p_pos = player:get_pos() - local cb = player:get_properties().collisionbox - - -- collect the positions of the nodes below the player's feet - local feet_poss = { - v_new(math.round(p_pos.x + cb[1]), math.ceil(p_pos.y + cb[2] - 0.5), math.round(p_pos.z + cb[3])), - v_new(math.round(p_pos.x + cb[1]), math.ceil(p_pos.y + cb[2] - 0.5), math.round(p_pos.z + cb[6])), - v_new(math.round(p_pos.x + cb[4]), math.ceil(p_pos.y + cb[2] - 0.5), math.round(p_pos.z + cb[3])), - v_new(math.round(p_pos.x + cb[4]), math.ceil(p_pos.y + cb[2] - 0.5), math.round(p_pos.z + cb[6])), - } - - for _, feet_pos in ipairs(feet_poss) do - if is_pos_on_ground(feet_pos, futil.box_offset(cb, p_pos)) then - return true - end - end - - return false -end - -function futil.get_object_center(object) - local pos = object:get_pos() - if not pos then - return - end - local cb = object:get_properties().collisionbox - return v_new(pos.x + (cb[1] + cb[4]) / 2, pos.y + (cb[2] + cb[5]) / 2, pos.z + (cb[3] + cb[6]) / 2) -end - -function futil.is_player(obj) - return minetest.is_player(obj) and not obj.is_fake_player -end - -function futil.is_valid_object(obj) - return obj and type(obj.get_pos) == "function" and vector.check(obj:get_pos()) -end - --- this is meant to be able to get the HP of any object, including "immortal" ones whose health is managed themselves --- it is *NOT* complete - i've got no idea where every mob API stores its hp. --- "health" is mobs_redo (which is actually redundant with `:get_hp()` because they're not actually immortal. --- "hp" is mobkit (and petz, which comes with its own fork of mobkit), and also creatura. -function futil.get_hp(obj) - if not futil.is_valid_object(obj) then - -- not an object or dead - return 0 - end - local ent = obj:get_luaentity() - if ent and (type(ent.hp) == "number" or type(ent.health) == "number") then - return ent.hp or ent.health - end - local armor_groups = obj:get_armor_groups() - if (armor_groups["immortal"] or 0) == 0 then - return obj:get_hp() - end - return math.huge -- presumably actually immortal -end diff --git a/mods/futil/minetest/object_properties.lua b/mods/futil/minetest/object_properties.lua deleted file mode 100644 index 754bb2a5..00000000 --- a/mods/futil/minetest/object_properties.lua +++ /dev/null @@ -1,158 +0,0 @@ -local f = string.format - -local iall = futil.functional.iall -local map = futil.map - -local in_bounds = futil.math.in_bounds - -local is_integer = futil.math.is_integer -local is_number = futil.is_number -local is_string = futil.is_string -local is_table = futil.is_table - -local function valid_box(value) - if value == nil then - return true - elseif not is_table(value) then - return false - elseif #value ~= 6 then - return false - else - return iall(map(is_number, value)) - end -end - -local function valid_visual_size(value) - if not is_table(value) then - return false - end - - local z_type = type(value.z) - return is_number(value.x) and is_integer(value.y) and (z_type == "number" or z_type == nil) -end - -local function valid_textures(value) - if not is_table(value) then - return false - end - - return iall(map(is_string, value)) -end - -local function valid_color_spec(value) - local t = type(value) - if t == "string" then - -- TODO: we could check for valid values, but that's ... tedious - return true - elseif t == "table" then - local is_number_ = is_number - local is_integer_ = is_integer - local in_bounds_ = in_bounds - local x = value.x - local y = value.y - local z = value.z - local a = value.a - - return ( - is_number_(x) - and in_bounds_(0, x, 255) - and is_integer_(x) - and is_number_(y) - and in_bounds_(0, y, 255) - and is_integer_(y) - and is_number_(z) - and in_bounds_(0, z, 255) - and is_integer_(z) - and (a == nil or (is_number_(a) and in_bounds_(0, a, 255) and is_integer_(a))) - ) - end - - return false -end - -local function valid_colors(value) - if not is_table(value) then - return false - end - - return iall(map(valid_color_spec, value)) -end - -local function valid_spritediv(value) - if not is_table(value) then - return false - end - - local x = value.x - local y = value.y - - return is_number(x) and is_integer(x) and is_number(y) and is_number(y) -end - -local function valid_automatic_face_movement_dir(value) - return value == false or is_number(value) -end - -local function valid_hp_max(value) - return is_number(value) and is_integer(value) and in_bounds(1, value, 65535) -end - -local object_property = { - visual = "string", - visual_size = valid_visual_size, - mesh = "string", - textures = valid_textures, - colors = valid_colors, - use_texture_alpha = "boolean", - spritediv = valid_spritediv, - initial_sprite_basepos = valid_spritediv, - is_visible = "boolean", - automatic_rotate = "number", - automatic_face_movement_dir = valid_automatic_face_movement_dir, - automatic_face_movement_max_rotation_per_sec = "number", - backface_culling = "number", - glow = "number", - damage_texture_modifier = "string", - shaded = "boolean", - - hp_max = valid_hp_max, - physical = "boolean", - pointable = "boolean", - collide_with_objects = "boolean", - collisionbox = valid_box, - selectionbox = valid_box, - - makes_footstep_sound = "boolean", - - stepheight = "number", - - nametag = "string", - nametag_color = valid_color_spec, - nametag_bgcolor = valid_color_spec, - - infotext = "string", - - static_save = "boolean", - - show_on_minimap = "boolean", -} - -function futil.is_property_key(key) - return object_property[key] ~= nil -end - -function futil.is_valid_property_value(key, value) - local kind = object_property[key] - - if not kind then - return false - end - - if type(kind) == "string" then - return type(value) == kind - elseif type(kind) == "function" then - return kind(value) - else - error(f("coding error in futil for key %q", key)) - end -end diff --git a/mods/futil/minetest/raycast.lua b/mods/futil/minetest/raycast.lua deleted file mode 100644 index e50a42a8..00000000 --- a/mods/futil/minetest/raycast.lua +++ /dev/null @@ -1,30 +0,0 @@ --- before 5.9, raycasts can miss objects they should hit if the cast is too short --- see https://github.com/minetest/minetest/issues/14337 -function futil.safecast(start, stop, objects, liquids, margin) - margin = margin or 5 - local ray = stop - start - local ray_length = ray:length() - if ray_length == 0 then - return function() end - elseif ray_length >= margin then - return Raycast(start, stop, objects, liquids) - end - - local actual_stop = start + ray:normalize() * margin - local raycast = Raycast(start, actual_stop, objects, liquids) - local stopped = false - return function() - if stopped then - return - end - local pt = raycast() - if pt then - local ip = pt.intersection_point - if (ip - start):length() > ray_length then - stopped = true - return - end - return pt - end - end -end diff --git a/mods/futil/minetest/registration.lua b/mods/futil/minetest/registration.lua deleted file mode 100644 index c17d5ee5..00000000 --- a/mods/futil/minetest/registration.lua +++ /dev/null @@ -1,15 +0,0 @@ -function futil.make_registration() - local t = {} - local registerfunc = function(func) - t[#t + 1] = func - end - return t, registerfunc -end - -function futil.make_registration_reverse() - local t = {} - local registerfunc = function(func) - table.insert(t, 1, func) - end - return t, registerfunc -end diff --git a/mods/futil/minetest/serialization.lua b/mods/futil/minetest/serialization.lua deleted file mode 100644 index 37951ecd..00000000 --- a/mods/futil/minetest/serialization.lua +++ /dev/null @@ -1,87 +0,0 @@ -local f = string.format - -local deserialize = minetest.deserialize - -local pairs_by_key = futil.table.pairs_by_key - -function futil.serialize(x) - if type(x) == "number" or type(x) == "boolean" or type(x) == "nil" then - return tostring(x) - elseif type(x) == "string" then - return f("%q", x) - elseif type(x) == "table" then - local parts = {} - for k, v in pairs_by_key(x) do - table.insert(parts, f("[%s] = %s", futil.serialize(k), futil.serialize(v))) - end - return f("{%s}", table.concat(parts, ", ")) - else - error(f("can't serialize type %s", type(x))) - end -end - -function futil.deserialize(data) - return deserialize(f("return %s", data)) -end - -function futil.serialize_invlist(inv, listname) - local itemstrings = {} - local list = inv:get_list(listname) - - if not list then - error(f("couldn't find list %s of %s", listname, minetest.write_json(inv:get_location()))) - end - - for _, stack in ipairs(list) do - table.insert(itemstrings, stack:to_string()) - end - - return futil.serialize(itemstrings) -end - -function futil.deserialize_invlist(serialized_list, inv, listname) - if not inv:is_empty(listname) then - error(("trying to deserialize into a non-empty list %s (%s)"):format(listname, serialized_list)) - end - - local itemstrings = futil.deserialize(serialized_list) or minetest.parse_json(serialized_list) - - inv:set_size(listname, #itemstrings) - - for i, itemstring in ipairs(itemstrings) do - inv:set_stack(listname, i, ItemStack(itemstring)) - end -end - -function futil.serialize_inv(inv) - local serialized_lists = {} - - for listname in pairs(inv:get_lists()) do - serialized_lists[listname] = futil.serialize_invlist(inv, listname) - end - - return futil.serialize(serialized_lists) -end - -function futil.deserialize_inv(serialized_lists, inv) - for listname, serialized_list in pairs(futil.deserialize(serialized_lists)) do - futil.deserialize_invlist(serialized_list, inv, listname) - end -end - -function futil.serialize_node_meta(pos) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - return futil.serialize({ - fields = meta:to_table().fields, - inventory = futil.serialize_inv(inv), - }) -end - -function futil.deserialize_node_meta(serialized_node_meta, pos) - local meta = minetest.get_meta(pos) - local x = futil.deserialize(serialized_node_meta) - meta:from_table({ fields = x.fields }) - local inv = meta:get_inventory() - futil.deserialize_inv(x.inventory, inv) -end diff --git a/mods/futil/minetest/strip_translation.lua b/mods/futil/minetest/strip_translation.lua deleted file mode 100644 index 4271ff24..00000000 --- a/mods/futil/minetest/strip_translation.lua +++ /dev/null @@ -1,205 +0,0 @@ -local function tokenize(s) - local tokens = {} - - local i = 1 - local j = 1 - - while true do - if s:sub(j, j) == "" then - if i < j then - table.insert(tokens, s:sub(i, j - 1)) - end - return tokens - elseif s:sub(j, j):byte() == 27 then - if i < j then - table.insert(tokens, s:sub(i, j - 1)) - end - - i = j - local n = s:sub(i + 1, i + 1) - - if n == "(" then - local m = s:sub(i + 2, i + 2) - local k = s:find(")", i + 3, true) - if not k then - futil.log("error", "strip_translation: couldn't tokenize %q", s) - return {} - end - if m == "T" then - table.insert(tokens, { - type = "translation", - domain = s:sub(i + 4, k - 1), - }) - elseif m == "c" then - table.insert(tokens, { - type = "color", - color = s:sub(i + 4, k - 1), - }) - elseif m == "b" then - table.insert(tokens, { - type = "bgcolor", - color = s:sub(i + 4, k - 1), - }) - else - futil.log("error", "strip_translation: couldn't tokenize %q", s) - return {} - end - i = k + 1 - j = k + 1 - elseif n == "F" then - table.insert(tokens, { - type = "start", - }) - i = j + 2 - j = j + 2 - elseif n == "E" then - table.insert(tokens, { - type = "stop", - }) - i = j + 2 - j = j + 2 - else - futil.log("error", "strip_translation: couldn't tokenize %q", s) - return {} - end - else - j = j + 1 - end - end -end - -local function parse(tokens, i, parsed) - parsed = parsed or {} - i = i or 1 - while i <= #tokens do - local token = tokens[i] - if type(token) == "string" then - table.insert(parsed, token) - i = i + 1 - elseif token.type == "color" or token.type == "bgcolor" then - table.insert(parsed, token) - i = i + 1 - elseif token.type == "translation" then - local contents = { - type = "translation", - domain = token.domain, - } - i = i + 1 - contents, i = parse(tokens, i, contents) - if i == -1 then - return "", -1 - end - table.insert(parsed, contents) - elseif token.type == "start" then - local contents = { - type = "escape", - } - i = i + 1 - contents, i = parse(tokens, i, contents) - if i == -1 then - return "", -1 - end - table.insert(parsed, contents) - elseif token.type == "stop" then - i = i + 1 - return parsed, i - else - futil.log("error", "strip_translation: couldn't parse %s", dump(token):gsub("%s+", "")) - return "", -1 - end - end - return parsed, i -end - -local function unparse_and_strip_translation(parsed, parts) - parts = parts or {} - for _, part in ipairs(parsed) do - if type(part) == "string" then - table.insert(parts, part) - else - if part.type == "bgcolor" then - table.insert(parts, ("\27(b@%s)"):format(part.color)) - elseif part.type == "color" then - table.insert(parts, ("\27(c@%s)"):format(part.color)) - elseif part.domain then - unparse_and_strip_translation(part, parts) - else - unparse_and_strip_translation(part, parts) - end - end - end - - return parts -end - -local function erase_after_newline(parsed, erasing) - local single_line_parsed = {} - - for _, piece in ipairs(parsed) do - if type(piece) == "string" then - if not erasing then - if piece:find("\n") then - erasing = true - local single_line = piece:match("^([^\n]*)\n") - table.insert(single_line_parsed, single_line) - else - table.insert(single_line_parsed, piece) - end - end - elseif piece.type == "bgcolor" or piece.type == "color" then - table.insert(single_line_parsed, piece) - elseif piece.type == "escape" then - table.insert(single_line_parsed, erase_after_newline(piece, erasing)) - elseif piece.type == "translation" then - local stuff = erase_after_newline(piece, erasing) - stuff.domain = piece.domain - table.insert(single_line_parsed, stuff) - else - futil.log("error", "strip_translation: couldn't erase_after_newline %s", dump(parsed):gsub("%s+", "")) - return {} - end - end - - return single_line_parsed -end - -local function unparse(parsed, parts) - parts = parts or {} - for _, part in ipairs(parsed) do - if type(part) == "string" then - table.insert(parts, part) - else - if part.type == "bgcolor" then - table.insert(parts, ("\27(b@%s)"):format(part.color)) - elseif part.type == "color" then - table.insert(parts, ("\27(c@%s)"):format(part.color)) - elseif part.domain then - table.insert(parts, ("\27(T@%s)"):format(part.domain)) - unparse(part, parts) - table.insert(parts, "\27E") - else - table.insert(parts, "\27F") - unparse(part, parts) - table.insert(parts, "\27E") - end - end - end - - return parts -end - -function futil.strip_translation(msg) - local tokens = tokenize(msg) - local parsed = parse(tokens) - return table.concat(unparse_and_strip_translation(parsed), "") -end - -function futil.get_safe_short_description(item) - item = type(item) == "userdata" and item or ItemStack(item) - local description = item:get_description() - local tokens = tokenize(description) - local parsed = parse(tokens) - local single_line_parsed = erase_after_newline(parsed) - local single_line = table.concat(unparse(single_line_parsed), "") - return single_line -end diff --git a/mods/futil/minetest/texture.lua b/mods/futil/minetest/texture.lua deleted file mode 100644 index ba53fe69..00000000 --- a/mods/futil/minetest/texture.lua +++ /dev/null @@ -1,9 +0,0 @@ --- https://github.com/minetest/minetest/blob/9fc018ded10225589d2559d24a5db739e891fb31/doc/lua_api.txt#L453-L462 -function futil.escape_texture(texturestring) - -- store in a variable so we don't return both rvs of gsub - local v = texturestring:gsub("[%^:]", { - ["^"] = "\\^", - [":"] = "\\:", - }) - return v -end diff --git a/mods/futil/minetest/time.lua b/mods/futil/minetest/time.lua deleted file mode 100644 index 767bcc1e..00000000 --- a/mods/futil/minetest/time.lua +++ /dev/null @@ -1,7 +0,0 @@ -function futil.wait(us) - local wait_until = minetest.get_us_time() + us - local get_us_time = minetest.get_us_time - while get_us_time() < wait_until do - -- the NOTHING function does nothing. - end -end diff --git a/mods/futil/minetest/vector.lua b/mods/futil/minetest/vector.lua deleted file mode 100644 index 5090f19b..00000000 --- a/mods/futil/minetest/vector.lua +++ /dev/null @@ -1,402 +0,0 @@ -local m_abs = math.abs -local m_acos = math.acos -local m_asin = math.asin -local m_atan2 = math.atan2 -local m_cos = math.cos -local m_floor = math.floor -local m_min = math.min -local m_max = math.max -local m_pi = math.pi -local m_pow = math.pow -local m_random = math.random -local m_sin = math.sin - -local v_add = vector.add -local v_new = vector.new -local v_sort = vector.sort -local v_sub = vector.subtract - -local in_bounds = futil.math.in_bounds -local bound = futil.math.bound - -local mapblock_size = 16 -- can be redefined, but effectively hard-coded -local chunksize = m_floor(tonumber(minetest.settings:get("chunksize")) or 5) -- # of mapblocks in a chunk (1 dim) -local chunksize_nodes = mapblock_size * chunksize -- # of nodes in a chunk (1 dim) -local max_mapgen_limit = 31007 -- hard coded -local mapgen_limit = - bound(0, m_floor(tonumber(minetest.settings:get("mapgen_limit")) or max_mapgen_limit), max_mapgen_limit) -local mapgen_limit_b = m_floor(mapgen_limit / mapblock_size) -- # of mapblocks - --- *actual* minimum and maximum coordinates - one mapblock short of the theoretical min and max -local map_min_i = (-mapgen_limit_b * mapblock_size) + chunksize_nodes -local map_max_i = ((mapgen_limit_b + 1) * mapblock_size - 1) - chunksize_nodes - -local map_min_p = v_new(map_min_i, map_min_i, map_min_i) -local map_max_p = v_new(map_max_i, map_max_i, map_max_i) - -futil.vector = {} - -function futil.vector.get_bounds(pos, radius) - return v_sub(pos, radius), v_add(pos, radius) -end - -futil.get_bounds = futil.vector.get_bounds - -function futil.vector.get_world_bounds() - return map_min_p, map_max_p -end - -futil.get_world_bounds = futil.vector.get_world_bounds - -function futil.vector.get_blockpos(pos) - return v_new(m_floor(pos.x / mapblock_size), m_floor(pos.y / mapblock_size), m_floor(pos.z / mapblock_size)) -end - -futil.get_blockpos = futil.vector.get_blockpos - -function futil.vector.get_block_min(blockpos) - return v_new(blockpos.x * mapblock_size, blockpos.y * mapblock_size, blockpos.z * mapblock_size) -end - -function futil.vector.get_block_max(blockpos) - return v_new( - blockpos.x * mapblock_size + (mapblock_size - 1), - blockpos.y * mapblock_size + (mapblock_size - 1), - blockpos.z * mapblock_size + (mapblock_size - 1) - ) -end - -function futil.vector.get_block_bounds(blockpos) - return futil.vector.get_block_min(blockpos), futil.vector.get_block_max(blockpos) -end - -futil.get_block_bounds = futil.vector.get_block_bounds - -function futil.vector.get_block_center(blockpos) - return v_add(futil.vector.get_block_min(blockpos), 8) -- 8 = 16 / 2 -end - -function futil.vector.get_chunkpos(pos) - return v_new( - m_floor((pos.x - map_min_i) / chunksize_nodes), - m_floor((pos.y - map_min_i) / chunksize_nodes), - m_floor((pos.z - map_min_i) / chunksize_nodes) - ) -end - -futil.get_chunkpos = futil.vector.get_chunkpos - -function futil.vector.get_chunk_bounds(chunkpos) - return v_new( - chunkpos.x * chunksize_nodes + map_min_i, - chunkpos.y * chunksize_nodes + map_min_i, - chunkpos.z * chunksize_nodes + map_min_i - ), - v_new( - chunkpos.x * chunksize_nodes + map_min_i + (chunksize_nodes - 1), - chunkpos.y * chunksize_nodes + map_min_i + (chunksize_nodes - 1), - chunkpos.z * chunksize_nodes + map_min_i + (chunksize_nodes - 1) - ) -end - -futil.get_chunk_bounds = futil.vector.get_chunk_bounds - -function futil.vector.formspec_pos(pos) - return ("%i,%i,%i"):format(pos.x, pos.y, pos.z) -end - -futil.formspec_pos = futil.vector.formspec_pos - -function futil.vector.iterate_area(minp, maxp) - minp, maxp = v_sort(minp, maxp) - local min_x = minp.x - local min_z = minp.z - - local x = min_x - 1 - local y = minp.y - local z = min_z - - local max_x = maxp.x - local max_y = maxp.y - local max_z = maxp.z - - return function() - if y > max_y then - return - end - - x = x + 1 - if x > max_x then - x = min_x - z = z + 1 - end - - if z > max_z then - z = min_z - y = y + 1 - end - - if y <= max_y then - return v_new(x, y, z) - end - end -end - -futil.iterate_area = futil.vector.iterate_area - -function futil.vector.iterate_volume(pos, radius) - return futil.iterate_area(futil.get_bounds(pos, radius)) -end - -futil.iterate_volume = futil.vector.iterate_volume - -function futil.is_pos_in_bounds(minp, pos, maxp) - minp, maxp = v_sort(minp, maxp) - return (in_bounds(minp.x, pos.x, maxp.x) and in_bounds(minp.y, pos.y, maxp.y) and in_bounds(minp.z, pos.z, maxp.z)) -end - -function futil.vector.is_inside_world_bounds(pos) - return futil.is_pos_in_bounds(map_min_p, pos, map_max_p) -end - -function futil.vector.is_blockpos_inside_world_bounds(blockpos) - return futil.vector.is_inside_world_bounds(futil.vector.get_block_min(blockpos)) -end - -futil.is_inside_world_bounds = futil.vector.is_inside_world_bounds - -function futil.vector.bound_position_to_world(pos) - return v_new( - bound(map_min_i, pos.x, map_max_i), - bound(map_min_i, pos.y, map_max_i), - bound(map_min_i, pos.z, map_max_i) - ) -end - -futil.bound_position_to_world = futil.vector.bound_position_to_world - -function futil.vector.volume(pos1, pos2) - local minp, maxp = v_sort(pos1, pos2) - return (maxp.x - minp.x + 1) * (maxp.y - minp.y + 1) * (maxp.z - minp.z + 1) -end - -function futil.split_region_by_mapblock(pos1, pos2, num_blocks) - local chunk_size = 16 * (num_blocks or 1) - local chunk_span = chunk_size - 1 - - pos1, pos2 = vector.sort(pos1, pos2) - - local min_x = pos1.x - local min_y = pos1.y - local min_z = pos1.z - local max_x = pos2.x - local max_y = pos2.y - local max_z = pos2.z - - local x1 = min_x - (min_x % chunk_size) - local x2 = max_x - (max_x % chunk_size) + chunk_span - local y1 = min_y - (min_y % chunk_size) - local y2 = max_y - (max_y % chunk_size) + chunk_span - local z1 = min_z - (min_z % chunk_size) - local z2 = max_z - (max_z % chunk_size) + chunk_span - - local chunks = {} - for y = y1, y2, chunk_size do - local y_min = m_max(min_y, y) - local y_max = m_min(max_y, y + chunk_span) - - for x = x1, x2, chunk_size do - local x_min = m_max(min_x, x) - local x_max = m_min(max_x, x + chunk_span) - - for z = z1, z2, chunk_size do - local z_min = m_max(min_z, z) - local z_max = m_min(max_z, z + chunk_span) - - chunks[#chunks + 1] = { v_new(x_min, y_min, z_min), v_new(x_max, y_max, z_max) } - end - end - end - - return chunks -end - -function futil.random_unit_vector() - local u = m_random() - local v = m_random() - local lambda = m_acos(2 * u - 1) - (m_pi / 2) - local phi = 2 * m_pi * v - return v_new(m_cos(lambda) * m_cos(phi), m_cos(lambda) * m_sin(phi), m_sin(lambda)) -end - ----- https://math.stackexchange.com/a/205589 ---function futil.random_unit_vector_in_solid_angle(theta, direction) --- local z = m_random() * (1 - m_cos(theta)) - 1 --- local phi = m_random() * 2 * m_pi --- local z2 = (1 - z*z) ^ 0.5 --- local ruv = v_new(z2 * m_cos(phi), z2 * m_sin(phi), z) --- direction = direction:normalize() --- ... ---end - -function futil.is_indoors(pos, distance, trials, hits_needed) - distance = distance or 20 - trials = trials or 11 - hits_needed = hits_needed or 9 - local num_hits = 0 - for _ = 1, trials do - local ruv = futil.random_unit_vector() - local target = pos + (distance * ruv) - local hit = Raycast(pos, target, false, false)() - if hit then - num_hits = num_hits + 1 - if num_hits == hits_needed then - return true - end - end - end - return false -end - -function futil.can_see_sky(pos, distance, trials, max_hits) - distance = distance or 200 - trials = trials or 11 - max_hits = max_hits or 9 - local num_hits = 0 - for _ = 1, trials do - local ruv = futil.random_unit_vector() - ruv.y = m_abs(ruv.y) -- look up, not at the ground - local target = pos + (distance * ruv) - local hit = Raycast(pos, target, false, false)() - if hit then - num_hits = num_hits + 1 - if num_hits > max_hits then - return false - end - end - end - return true -end - -function futil.vector.is_valid_position(pos) - if type(pos) ~= "table" then - return false - elseif not (type(pos.x) == "number" and type(pos.y) == "number" and type(pos.z) == "number") then - return false - else - return futil.is_inside_world_bounds(vector.round(pos)) - end -end - --- minetest.hash_node_position only works with integer coordinates -function futil.vector.hash(pos) - return string.format("%a:%a:%a", pos.x, pos.y, pos.z) -end - -function futil.vector.unhash(string) - local x, y, z = string:match("^([^:]+):([^:]+):([^:]+)$") - x, y, z = tonumber(x), tonumber(y), tonumber(z) - if not (x and y and z) then - return - end - return v_new(x, y, z) -end - -function futil.vector.ldistance(pos1, pos2, p) - if p == math.huge then - return m_max(m_abs(pos1.x - pos2.x), m_abs(pos1.y - pos2.y), m_abs(pos1.z - pos2.z)) - else - return m_pow( - m_pow(m_abs(pos1.x - pos2.x), p) + m_pow(m_abs(pos1.y - pos2.y), p) + m_pow(m_abs(pos1.z - pos2.z), p), - 1 / p - ) - end -end - -function futil.vector.round(pos, mult) - local round = futil.math.round - return v_new(round(pos.x, mult), round(pos.y, mult), round(pos.z, mult)) -end - --- https://msl.cs.uiuc.edu/planning/node102.html -function futil.vector.rotation_to_matrix(rotation) - local cosp = m_cos(rotation.x) - local sinp = m_sin(rotation.x) - local pitch = { - { cosp, 0, sinp }, - { 0, 1, 0 }, - { -sinp, 0, cosp }, - } - local cosy = m_cos(rotation.y) - local siny = m_sin(rotation.y) - local yaw = { - { cosy, -siny, 0 }, - { siny, cosy, 0 }, - { 0, 0, 1 }, - } - local cosr = m_cos(rotation.z) - local sinr = m_sin(rotation.z) - local roll = { - { 1, 0, 0 }, - { 0, cosr, -sinr }, - { 0, sinr, cosr }, - } - return futil.matrix.multiply(futil.matrix.multiply(yaw, pitch), roll) -end - --- https://msl.cs.uiuc.edu/planning/node103.html -function futil.vector.matrix_to_rotation(matrix) - local pitch = m_atan2(matrix[2][1], matrix[1][1]) - local yaw = m_asin(-matrix[3][1]) - local roll = m_atan2(matrix[3][2], matrix[3][3]) - return v_new(pitch, yaw, roll) -end - -function futil.vector.inverse_rotation(rot) - -- since the determinant of a rotation matrix is 1, the inverse is just the transpose and i don't have to write - -- a matrix inverter - return futil.vector.matrix_to_rotation(futil.matrix.transpose(futil.vector.rotation_to_matrix(rot))) -end - --- assumed in radians -function futil.vector.compose_rotations(rot1, rot2) - local m1 = futil.vector.rotation_to_matrix(rot1) - local m2 = futil.vector.rotation_to_matrix(rot2) - return futil.vector.matrix_to_rotation(futil.matrix.multiply(m1, m2)) -end - --- https://palitri.com/vault/stuff/maths/Rays%20closest%20point.pdf --- this was originally part of the ballistics mod but i don't need it there anymore -function futil.vector.closest_point_to_two_lines(last_pos, last_vel, cur_pos, cur_vel, threshold) - threshold = threshold or 0.0001 -- if certain values are too close to 0, the results will not be good - local a = cur_vel - local b = last_vel - local a2 = a:dot(a) - if a2 < threshold then - return - end - local b2 = b:dot(b) - if b2 < threshold then - return - end - local ab = a:dot(b) - local denom = (a2 * b2) - (ab * ab) - if denom < threshold then - return - end - local A = cur_pos - local B = last_pos - local c = last_pos - cur_pos - local bc = b:dot(c) - local ac = a:dot(c) - local D = A + a * ((ac * b2 - ab * bc) / denom) - local E = B + b * ((ab * ac - bc * a2) / denom) - return (D + E) / 2 -end - -function futil.vector.v2f_to_float_32(v) - return { - x = futil.math.to_float32(v.x), - y = futil.math.to_float32(v.y), - } -end diff --git a/mods/futil/mod.conf b/mods/futil/mod.conf deleted file mode 100644 index 5a7a55d9..00000000 --- a/mods/futil/mod.conf +++ /dev/null @@ -1,11 +0,0 @@ -name = futil -title = futil -description = flux's utility mod -website = https://content.minetest.net/packages/rheo/futil/ -author = fluxionary -license = LGPL-3.0-or-later -media_license = CC-BY-SA-4.0 -version = 2024-03-30 -min_minetest_version = 5.8.0 -supported_games = * -depends = fmod diff --git a/mods/futil/util/bisect.lua b/mods/futil/util/bisect.lua deleted file mode 100644 index 1d9ee0ae..00000000 --- a/mods/futil/util/bisect.lua +++ /dev/null @@ -1,51 +0,0 @@ -futil.bisect = {} - -function futil.bisect.right(t, x, low, high, key) - low = low or 1 - high = high or #t + 1 - if key then - while low < high do - local mid = math.floor((low + high) / 2) - if x < key(t[mid]) then - high = mid - else - low = mid + 1 - end - end - else - while low < high do - local mid = math.floor((low + high) / 2) - if x < t[mid] then - high = mid - else - low = mid + 1 - end - end - end - return low -end - -function futil.bisect.left(t, x, low, high, key) - low = low or 1 - high = high or #t + 1 - if key then - while low < high do - local mid = math.floor((low + high) / 2) - if key(t[mid]) < x then - low = mid + 1 - else - high = mid - end - end - else - while low < high do - local mid = math.floor((low + high) / 2) - if t[mid] < x then - low = mid + 1 - else - high = mid - end - end - end - return low -end diff --git a/mods/futil/util/class.lua b/mods/futil/util/class.lua deleted file mode 100644 index fa8c004a..00000000 --- a/mods/futil/util/class.lua +++ /dev/null @@ -1,89 +0,0 @@ -function futil.class1(super) - local class = {} - class.__index = class -- this becomes the index "metamethod" of objects - - setmetatable(class, { - __index = super and super.__index or super, - __call = function(this_class, ...) - local obj = setmetatable({}, this_class) - local init = obj._init - if init then - init(obj, ...) - end - return obj - end, - }) - - function class:is_a(class2) - if class == class2 then - return true - end - - if super and super:is_a(class2) then - return true - end - - return false - end - - return class -end - -function futil.class(...) - local class = {} - class.__index = class - - local meta = { - __call = function(this_class, ...) - local obj = setmetatable({}, this_class) - local init = obj._init - if init then - init(obj, ...) - end - return obj - end, - } - - local parents = { ... } - class._parents = parents - - if #parents > 0 then - function meta:__index(key) - for i = #parents, 1, -1 do - local parent = parents[i] - local index = parent.__index - local v - if index then - if type(index) == "function" then - v = index(self, key) - else - v = index[key] - end - else - v = parent[key] - end - if v then - return v - end - end - end - end - - setmetatable(class, meta) - - function class:is_a(class2) - if class == class2 then - return true - end - - for _, parent in ipairs(parents) do - if parent:is_a(class2) then - return true - end - end - - return false - end - - return class -end diff --git a/mods/futil/util/coalesce.lua b/mods/futil/util/coalesce.lua deleted file mode 100644 index 1491e3af..00000000 --- a/mods/futil/util/coalesce.lua +++ /dev/null @@ -1,9 +0,0 @@ -function futil.coalesce(...) - local arg = futil.table.pack(...) - for i = 1, arg.n do - local v = arg[i] - if v ~= nil then - return v - end - end -end diff --git a/mods/futil/util/equals.lua b/mods/futil/util/equals.lua deleted file mode 100644 index c5587b3e..00000000 --- a/mods/futil/util/equals.lua +++ /dev/null @@ -1,28 +0,0 @@ -local table_size = futil.table.size - -local function equals(a, b) - local t = type(a) - - if t ~= type(b) then - return false - end - - if t ~= "table" then - return a == b - elseif a == b then - return true - end - - local size_a = 0 - - for key, value in pairs(a) do - if not equals(value, b[key]) then - return false - end - size_a = size_a + 1 - end - - return size_a == table_size(b) -end - -futil.equals = equals diff --git a/mods/futil/util/exception.lua b/mods/futil/util/exception.lua deleted file mode 100644 index 45800a42..00000000 --- a/mods/futil/util/exception.lua +++ /dev/null @@ -1,29 +0,0 @@ -function futil.safe_wrap(func, rv_on_fail, error_callback) - -- wrap a function w/ logic to avoid crashing - return function(...) - local rvs = { xpcall(func, debug.traceback, ...) } - - if rvs[1] then - return unpack(rvs, 2) - else - if error_callback then - error_callback(debug.getinfo(func), { ... }, rvs[2]) - else - futil.log( - "error", - "(check_call): %s args: %s out: %s", - dump(debug.getinfo(func)), - dump({ ... }), - rvs[2] - ) - end - return rv_on_fail - end - end -end - -function futil.safe_call(func, rv_on_fail, error_callback, ...) - return futil.safe_wrap(func, rv_on_fail, error_callback)(...) -end - -futil.check_call = futil.safe_wrap -- backwards compatibility diff --git a/mods/futil/util/file.lua b/mods/futil/util/file.lua deleted file mode 100644 index 999f97b7..00000000 --- a/mods/futil/util/file.lua +++ /dev/null @@ -1,37 +0,0 @@ -function futil.file_exists(path) - local f = io.open(path, "r") - if f then - io.close(f) - return true - else - return false - end -end - -function futil.load_file(filename) - local file = io.open(filename, "r") - - if not file then - return - end - - local contents = file:read("*a") - - file:close() - - return contents -end - --- minetest.safe_file_write is apparently unreliable on windows -function futil.write_file(filename, contents) - local file = io.open(filename, "w") - - if not file then - return false - end - - file:write(contents) - file:close() - - return true -end diff --git a/mods/futil/util/functional.lua b/mods/futil/util/functional.lua deleted file mode 100644 index 9fc56cd0..00000000 --- a/mods/futil/util/functional.lua +++ /dev/null @@ -1,159 +0,0 @@ -local functional = {} - -local t_iterate = futil.table.iterate -local t_insert = table.insert - -function functional.noop() - -- the NOTHING function does nothing. -end - -function functional.identity(x) - return x -end - -function functional.izip(...) - local is = { ... } - if #is == 0 then - return functional.noop - end - - return function() - local t = {} - for i in t_iterate(is) do - local v = i() - if v ~= nil then - t_insert(t, v) - else - return - end - end - - return t - end -end - -function functional.zip(...) - local is = {} - for t in t_iterate({ ... }) do - t_insert(is, t_iterate(t)) - end - return functional.izip(unpack(is)) -end - -function functional.imap(func, ...) - local zipper = functional.izip(...) - return function() - local args = zipper() - if args then - return func(unpack(args)) - end - end -end - -function functional.map(func, ...) - local zipper = functional.zip(...) - return function() - local args = zipper() - if args then - return func(unpack(args)) - end - end -end - -function functional.apply(func, t) - local t2 = {} - for k, v in pairs(t) do - t2[k] = func(v) - end - return t2 -end - -function functional.reduce(func, t, initial) - local i = t_iterate(t) - if not initial then - initial = i() - end - local next = i() - while next do - initial = func(initial, next) - next = i() - end - return initial -end - -function functional.partial(func, ...) - local args = { ... } - return function(...) - return func(unpack(args), ...) - end -end - -function functional.compose(a, b) - return function(...) - return a(b(...)) - end -end - -function functional.ifilter(pred, i) - local v - return function() - v = i() - while v ~= nil and not pred(v) do - v = i() - end - return v - end -end - -function functional.filter(pred, t) - return functional.ifilter(pred, t_iterate(t)) -end - -function functional.iall(i) - while true do - local v = i() - if v == false then - return false - elseif v == nil then - return true - end - end -end - -function functional.all(t) - for i = 1, #t do - if not t[i] then - return false - end - end - - return true -end - -function functional.iany(i) - while true do - local v = i() - if v == nil then - return false - elseif v then - return true - end - end -end - -function functional.any(t) - for i = 1, #t do - if t[i] then - return true - end - end - return false -end - -function functional.wrap(f) - return function(...) - return f(...) - end -end - -futil.functional = functional diff --git a/mods/futil/util/http.lua b/mods/futil/util/http.lua deleted file mode 100644 index 3973d76b..00000000 --- a/mods/futil/util/http.lua +++ /dev/null @@ -1,10 +0,0 @@ -local function char_to_hex(c) - return string.format("%%%02X", string.byte(c)) -end - -function futil.urlencode(text) - text = text:gsub("\n", "\r\n") - text = text:gsub("([^0-9a-zA-Z !'()*._~-])", char_to_hex) - text = text:gsub(" ", "+") - return text -end diff --git a/mods/futil/util/init.lua b/mods/futil/util/init.lua deleted file mode 100644 index e886719a..00000000 --- a/mods/futil/util/init.lua +++ /dev/null @@ -1,24 +0,0 @@ -futil.dofile("util", "bisect") -futil.dofile("util", "class") -futil.dofile("util", "coalesce") -futil.dofile("util", "exception") -futil.dofile("util", "file") -futil.dofile("util", "http") -futil.dofile("util", "list") -futil.dofile("util", "math") -futil.dofile("util", "matrix") -futil.dofile("util", "memoization") -futil.dofile("util", "memory") -futil.dofile("util", "path") -futil.dofile("util", "predicates") -futil.dofile("util", "string") -futil.dofile("util", "table") - -futil.dofile("util", "equals") -- depends on table -futil.dofile("util", "functional") -- depends on table -futil.dofile("util", "iterators") -- depends on functional -futil.dofile("util", "random") -- depends on math -futil.dofile("util", "regex") -- depends on exception -futil.dofile("util", "selection") -- depends on table, math -futil.dofile("util", "time") -- depends on math -futil.dofile("util", "limiters") -- depends on functional diff --git a/mods/futil/util/iterators.lua b/mods/futil/util/iterators.lua deleted file mode 100644 index 0eeaba86..00000000 --- a/mods/futil/util/iterators.lua +++ /dev/null @@ -1,106 +0,0 @@ -local iterators = {} - -function iterators.range(...) - local a, b, c = ... - if type(a) ~= "number" then - error("invalid range") - end - if not b then - a, b = 1, a - end - if type(b) ~= "number" then - error("invalid range") - end - c = c or 1 - if type(c) ~= "number" or c == 0 then - error("invalid range") - end - - if c > 0 then - return function() - if a > b then - return - end - local to_return = a - a = a + c - return to_return - end - else - return function() - if a < b then - return - end - local to_return = a - a = a + c - return to_return - end - end -end - -function iterators.repeat_(value, times) - if times then - local i = 0 - return function() - i = i + 1 - if i <= times then - return value - end - end - else - return function() - return value - end - end -end - -function iterators.chain(...) - local arg = { ... } - local i = 1 - - return function() - while i <= #arg do - local v = arg[i]() - if v then - return v - end - end - end -end - -function iterators.count(start, step) - step = step or 1 - return function() - local rv = start - start = start + step - return rv - end -end - -function iterators.values(t) - local k - return function() - local value - k, value = next(t, k) - return value - end -end - -function iterators.accumulate(t, composer, initial) - local value = initial - local i = futil.table.iterate(t) - return function() - local next_value = i() - if next_value then - if value == nil then - value = next_value - elseif composer then - value = composer(value, next_value) - else - value = value + next_value - end - return value - end - end -end - -futil.iterators = iterators diff --git a/mods/futil/util/limiters.lua b/mods/futil/util/limiters.lua deleted file mode 100644 index 41db21a4..00000000 --- a/mods/futil/util/limiters.lua +++ /dev/null @@ -1,42 +0,0 @@ ---[[ - functions to limit the growth of a variable. - the intention here is to provide a family of functions for which - * f(x) is defined for all x >= 0 - * f(0) = 0 - * f(1) = 1 - * f is continuous - * f is nondecreasing - * f\'(x) is nonincreasing when x > 1 (when the parameters are appropriate) -]] - -local log = math.log -local pow = math.pow -local tanh = math.tanh - -futil.limiters = { - -- no limiting - none = futil.functional.identity, - -- f(x) = x ^ param_1. param_1 should be < 1 for f\'(x) to be nonincreasing - -- f(x) will grow arbitrarily, but at a decreasing rate. - gamma = function(x, param_1) - return pow(x, param_1) - end, - -- the hyperbolic tangent scaled so that f(0) = 0 and f(1) = 1. - -- f(x) will grow approximately linearly for small x, but it will never grow beyond a maximum value, which is - -- approximately equal to param_1 + 1 - tanh = function(x, param_1) - return (tanh((x - 1) / param_1) - tanh(-1 / param_1)) / -tanh(-1 / param_1) - end, - -- f(x) = log^param_2(param_1 * x + 1), scaled so that f(0) = 0 and f(1) = 1. - -- f(x) will grow arbitrarily, but at a much slower rate than a gamma limiter - log__n = function(x, param_1, param_2) - return (log(x + 1) * pow(log(param_1 * x + 1), param_2) / (log(2) * pow(log(param_1 + 1), param_2))) - end, -} - -function futil.create_limiter(name, param_1, param_2) - local f = futil.limiters[name] - return function(x) - return f(x, param_1, param_2) - end -end diff --git a/mods/futil/util/list.lua b/mods/futil/util/list.lua deleted file mode 100644 index ed8103fa..00000000 --- a/mods/futil/util/list.lua +++ /dev/null @@ -1,19 +0,0 @@ -function futil.list(iterator) - local t = {} - local v = iterator() - while v do - t[#t + 1] = v - v = iterator() - end - return t -end - -function futil.list_multiple(iterator) - local t = {} - local v = { iterator() } - while #v > 0 do - t[#t + 1] = v - v = { iterator() } - end - return t -end diff --git a/mods/futil/util/math.lua b/mods/futil/util/math.lua deleted file mode 100644 index f2df95e7..00000000 --- a/mods/futil/util/math.lua +++ /dev/null @@ -1,162 +0,0 @@ -futil.math = {} - -local floor = math.floor -local huge = math.huge -local max = math.max -local min = math.min - -function futil.math.idiv(a, b) - local rem = a % b - return (a - rem) / b, rem -end - -function futil.math.bound(m, v, M) - return max(m, min(v, M)) -end - -function futil.math.in_bounds(m, v, M) - return m <= v and v <= M -end - -local in_bounds = futil.math.in_bounds - -function futil.math.is_integer(v) - return floor(v) == v -end - -local is_integer = futil.math.is_integer - -function futil.math.is_u8(i) - return (type(i) == "number" and is_integer(i) and in_bounds(0, i, 0xFF)) -end - -function futil.math.is_u16(i) - return (type(i) == "number" and is_integer(i) and in_bounds(0, i, 0xFFFF)) -end - -function futil.math.sum(t, initial) - local sum - local start - if initial then - sum = initial - start = 1 - else - sum = t[1] - start = 2 - end - - for i = start, #t do - sum = sum + t[i] - end - - return sum -end - -function futil.math.isum(i, initial) - local sum - - if initial == nil then - sum = i() - else - sum = initial - end - - local v = i() - - while v do - sum = sum + v - v = i() - end - - return sum -end - -function futil.math.product(t, initial) - local product - local start - if initial then - product = initial - start = 1 - else - product = t[1] - start = 2 - end - - for i = start, #t do - product = product * t[i] - end - - return product -end - -function futil.math.iproduct(i, initial) - local product - - if initial == nil then - product = i() - else - product = initial - end - - local v = i() - - while v do - product = product * v - v = i() - end - - return product -end - -function futil.math.probabilistic_round(v) - return floor(v + math.random()) -end - -function futil.math.cmp(a, b) - return a < b -end - -futil.math.deg2rad = math.deg - -futil.math.rad2deg = math.rad - -function futil.math.do_intervals_overlap(min1, max1, min2, max2) - return min1 <= max2 and min2 <= max1 -end - --- i took one class from kahan and can't stop doing this -local function round(n) - local d = n % 1 - local i = n - d - - if i % 2 == 0 then - if d <= 0.5 then - return i - else - return i + 1 - end - else - if d < 0.5 then - return i - else - return i + 1 - end - end -end - -function futil.math.round(number, mult) - if mult then - return round(number / mult) * mult - else - return round(number) - end -end - --- TODO this doesn't handle out-of-bounds exponents -function futil.math.to_float32(number) - if number == huge or number == -huge or number ~= number then - return number - end - local sign, significand, exponent = ("%a"):format(number):match("^(-?)0x([0-9a-f\\.]+)p([0-9+-]+)$") - return tonumber(("%s0x%sp%s"):format(sign, significand:sub(1, 8), exponent)) -end diff --git a/mods/futil/util/matrix.lua b/mods/futil/util/matrix.lua deleted file mode 100644 index 433d9a7c..00000000 --- a/mods/futil/util/matrix.lua +++ /dev/null @@ -1,30 +0,0 @@ -futil.matrix = {} - -function futil.matrix.multiply(m1, m2) - assert(#m1[1] == #m2, "width of first argument must be height of second") - local product = {} - for i = 1, #m1 do - local row = {} - for j = 1, #m2[1] do - local value = 0 - for k = 1, #m2 do - value = value + m1[i][k] * m2[k][j] - end - row[j] = value - end - product[i] = row - end - return product -end - -function futil.matrix.transpose(m) - local t = {} - for i = 1, #m[1] do - local row = {} - for j = 1, #m do - row[j] = m[j][i] - end - t[i] = row - end - return t -end diff --git a/mods/futil/util/memoization.lua b/mods/futil/util/memoization.lua deleted file mode 100644 index 73a86163..00000000 --- a/mods/futil/util/memoization.lua +++ /dev/null @@ -1,51 +0,0 @@ -local private_state = ... -local mod_storage = private_state.mod_storage - -function futil.memoize1(func) - local memo = {} - return function(arg) - if arg == nil then - return func(arg) - end - local rv = memo[arg] - - if not rv then - rv = func(arg) - memo[arg] = rv - end - - return rv - end -end - -function futil.memoize_dumpable(func) - local memo = {} - return function(...) - local key = dump({ ... }) - local rv = memo[key] - - if not rv then - rv = func(...) - memo[key] = rv - end - - return rv - end -end - -function futil.memoize1_modstorage(id, func) - local key_format = ("%%s:%s:memoize"):format(id) - return function(arg) - local key_key = key_format:format(tostring(arg)) - local rv = mod_storage:get(key_key) - - if not rv then - rv = func(arg) - mod_storage:set_string(key_key, tostring(rv)) - end - - return rv - end -end - -futil.memoize1ms = futil.memoize1_modstorage -- backwards compatibility diff --git a/mods/futil/util/memory.lua b/mods/futil/util/memory.lua deleted file mode 100644 index 7ad6da8b..00000000 --- a/mods/futil/util/memory.lua +++ /dev/null @@ -1,45 +0,0 @@ --- i have no idea how accurate this is, i use documentation from the below link for a few things --- https://wowwiki-archive.fandom.com/wiki/Lua_object_memory_sizes - -local function estimate_memory_usage(thing, seen) - local typ = type(thing) - if typ == "nil" then - return 0 - end - - seen = seen or {} - if seen[thing] then - return 0 - end - seen[thing] = true - - if typ == "boolean" then - return 4 - elseif typ == "number" then - return 8 -- this is probably larger? - elseif typ == "string" then - return 25 + typ:len() - elseif typ == "function" then - -- TODO: we can calculate the usage of upvalues, but that's complicated - return 40 - elseif typ == "userdata" then - return 0 -- this is probably larger - elseif typ == "thread" then - return 1224 -- this is probably larger - elseif typ == "table" then - local size = 64 - for k, v in pairs(thing) do - if type(k) == "number" then - size = size + 16 + estimate_memory_usage(v, seen) - else - size = size + 40 + estimate_memory_usage(k, seen) + estimate_memory_usage(v, seen) - end - end - return size - else - futil.log("warning", "estimate_memory_usage: unknown type %s", typ) - return 0 -- ???? - end -end - -futil.estimate_memory_usage = estimate_memory_usage diff --git a/mods/futil/util/path.lua b/mods/futil/util/path.lua deleted file mode 100644 index 9c225742..00000000 --- a/mods/futil/util/path.lua +++ /dev/null @@ -1,7 +0,0 @@ -function futil.path_concat(...) - return table.concat({ ... }, DIR_DELIM) -end - -function futil.path_split(path) - return string.split(path, DIR_DELIM, true) -end diff --git a/mods/futil/util/predicates.lua b/mods/futil/util/predicates.lua deleted file mode 100644 index 61aa26b2..00000000 --- a/mods/futil/util/predicates.lua +++ /dev/null @@ -1,43 +0,0 @@ -function futil.is_nil(v) - return v == nil -end - -function futil.is_boolean(v) - return v == true or v == false -end - -function futil.is_number(v) - return type(v) == "number" -end - -function futil.is_positive(v) - return v > 0 -end - -function futil.is_integer(v) - return v % 1 == 0 -end - -function futil.is_positive_integer(v) - return type(v) == "number" and v > 0 and v % 1 == 0 -end - -function futil.is_string(v) - return type(v) == "string" -end - -function futil.is_userdata(v) - return type(v) == "userdata" -end - -function futil.is_function(v) - return type(v) == "function" -end - -function futil.is_thread(v) - return type(v) == "thread" -end - -function futil.is_table(v) - return type(v) == "table" -end diff --git a/mods/futil/util/random.lua b/mods/futil/util/random.lua deleted file mode 100644 index 81a38321..00000000 --- a/mods/futil/util/random.lua +++ /dev/null @@ -1,84 +0,0 @@ -local f = string.format - -futil.random = {} - -function futil.random.choice(t, random) - random = random or math.random - return t[random(#t)] -end - -function futil.random.weighted_choice(t, random) - random = random or math.random - local elements, weights = {}, {} - local i = 1 - for element, weight in pairs(t) do - elements[i] = element - weights[i] = weight - i = i + 1 - end - local breaks = futil.list(futil.iterators.accumulate(weights)) - local value = random() * breaks[#breaks] - return elements[futil.bisect.right(breaks, value)] -end - -local WeightedChooser = futil.class1() - -function WeightedChooser:_init(t) - local elements, weights = {}, {} - local i = 1 - for element, weight in pairs(t) do - elements[i] = element - weights[i] = weight - i = i + 1 - end - self._elements = elements - self._breaks = futil.list(futil.iterators.accumulate(weights)) -end - -function WeightedChooser:next(random) - random = random or math.random - local breaks = self._breaks - local value = random() * breaks[#breaks] - return self._elements[futil.bisect.right(breaks, value)] -end - -futil.random.WeightedChooser = WeightedChooser - -function futil.random.choice(t, random) - assert(#t > 0, "cannot get choice from an empty table") - random = random or math.random - return t[random(#t)] -end - --- https://stats.stackexchange.com/questions/569647/ -function futil.random.sample(t, k, random) - assert(k <= #t, f("cannot sample %i items from a set of size %i", k, #t)) - random = random or math.random - local sample = {} - for i = 1, k do - sample[i] = t[i] - end - for j = k + 1, #t do - if random() < k / j then - sample[random(1, k)] = t[j] - end - end - - return sample -end - -function futil.random.sample_with_indices(t, k, random) - assert(k <= #t, f("cannot sample %i items from a set of size %i", k, #t)) - random = random or math.random - local sample = {} - for i = 1, k do - sample[i] = { i, t[i] } - end - for j = k + 1, #t do - if random() < k / j then - sample[random(1, k)] = { j, t[j] } - end - end - - return sample -end diff --git a/mods/futil/util/regex.lua b/mods/futil/util/regex.lua deleted file mode 100644 index f691c9f4..00000000 --- a/mods/futil/util/regex.lua +++ /dev/null @@ -1,6 +0,0 @@ -function futil.is_valid_regex(pattern) - return futil.safe_call(function() - (""):match(pattern) - return true - end, false, futil.functional.noop) -end diff --git a/mods/futil/util/selection.lua b/mods/futil/util/selection.lua deleted file mode 100644 index 3e2c99f7..00000000 --- a/mods/futil/util/selection.lua +++ /dev/null @@ -1,109 +0,0 @@ -local floor = math.floor -local min = math.min -local random = math.random - -local swap = futil.table.swap -local default_cmp = futil.math.cmp - -futil.selection = {} - -local function partition5(t, left, right, cmp) - cmp = cmp or default_cmp - local i = left + 1 - while i <= right do - local j = i - while j > left and cmp(t[j], t[j - 1]) do - swap(t, j - 1, j) - j = j - 1 - end - i = i + 1 - end - return floor((left + right) / 2) -end - -local function partition(t, left, right, pivot_i, i, cmp) - cmp = cmp or default_cmp - local pivot_v = t[pivot_i] - swap(t, pivot_i, right) - local store_i = left - for j = left, right - 1 do - if cmp(t[j], pivot_v) then - swap(t, store_i, j) - store_i = store_i + 1 - end - end - local store_i_eq = store_i - for j = store_i, right - 1 do - if t[j] == pivot_v then - swap(t, store_i_eq, j) - store_i_eq = store_i_eq + 1 - end - end - swap(t, right, store_i_eq) - if i < store_i then - return store_i - elseif i <= store_i_eq then - return i - else - return store_i_eq - end -end - -local function quickselect(t, left, right, i, pivot_alg, cmp) - cmp = cmp or default_cmp - while true do - if left == right then - return left - end - local pivot_i = partition(t, left, right, pivot_alg(t, left, right, cmp), i, cmp) - if i == pivot_i then - return i - elseif i < pivot_i then - right = pivot_i - 1 - else - left = pivot_i + 1 - end - end -end - -futil.selection.quickselect = quickselect - -futil.selection.pivot = {} - -function futil.selection.pivot.random(t, left, right, cmp) - return random(left, right) -end - -local function pivot_medians_of_medians(t, left, right, cmp) - cmp = cmp or default_cmp - if right - left < 5 then - return partition5(t, left, right, cmp) - end - for i = left, right, 5 do - local sub_right = min(i + 4, right) - local median5 = partition5(t, i, sub_right, cmp) - swap(t, median5, left + floor((i - left) / 5)) - end - local mid = floor((right - left) / 10) + left + 1 - return quickselect(t, left, left + floor((right - left) / 5), mid, pivot_medians_of_medians, cmp) -end - -futil.selection.pivot.median_of_medians = pivot_medians_of_medians - ---[[ -make use of quickselect to munge a table: - median_index = math.floor(#t / 2) - after calling this, - t[1] through t[median_index - 1] will be the elements less than t[median_index] - t[median_index] will be the median (or element less-than-the-median for even length tables) - t[median_index + 1] through t[#t] will be the elements greater than t[median_index] -pivot is a pivot algorithm, defaults to random selection -cmp is a comparison function. -returns median_index. -]] -function futil.selection.select(t, pivot_alg, cmp) - cmp = cmp or default_cmp - pivot_alg = pivot_alg or futil.selection.pivot.random - local median_index = math.floor(#t / 2) - return quickselect(t, 1, #t, median_index, pivot_alg, cmp) -end diff --git a/mods/futil/util/string.lua b/mods/futil/util/string.lua deleted file mode 100644 index 16877294..00000000 --- a/mods/futil/util/string.lua +++ /dev/null @@ -1,62 +0,0 @@ -futil.string = {} - -function futil.string.truncate(s, max_length, suffix) - suffix = suffix or "..." - - if s:len() > max_length then - return s:sub(1, max_length - suffix:len()) .. suffix - else - return s - end -end - -function futil.string.lc_cmp(a, b) - return a:lower() < b:lower() -end - -function futil.string.startswith(s, start, start_i, end_i) - return s:sub(start_i or 0, end_i or #s):sub(1, #start) == start -end - -local escape_pattern = "([%(%)%.%%%+%-%*%?%[%^%$])" -local function escape_regex(str) - return str:gsub(escape_pattern, "%%%1") -end - -local glob_patterns = { - ["?"] = ".", - ["*"] = ".*", -} - -local function transform_pattern(pattern) - local parts = {} - local start = 1 - for i = 1, #pattern do - local glob_pattern = glob_patterns[pattern:sub(i)] - if glob_pattern then - if start < i then - parts[#parts + 1] = escape_regex(pattern:sub(start, i - 1)) - end - parts[#parts + 1] = glob_pattern - start = i + 1 - end - end - if start < #pattern then - parts[#parts + 1] = escape_regex(pattern:sub(start, #pattern)) - end - return table.concat(parts, "") -end - -function futil.string.globmatch(str, pattern) - return str:match(transform_pattern(pattern)) -end - -futil.GlobMatcher = futil.class1() - -function futil.GlobMatcher:_init(pattern) - self._pattern = transform_pattern(pattern) -end - -function futil.GlobMatcher:match(str) - return str:match(self._pattern) -end diff --git a/mods/futil/util/table.lua b/mods/futil/util/table.lua deleted file mode 100644 index 0a2ae296..00000000 --- a/mods/futil/util/table.lua +++ /dev/null @@ -1,188 +0,0 @@ -local default_cmp = futil.math.cmp - -futil.table = {} - -function futil.table.set_all(t1, t2) - for k, v in pairs(t2) do - t1[k] = v - end - return t1 -end - -function futil.table.compose(t1, t2) - local t = table.copy(t1) - futil.table.set_all(t, t2) - return t -end - -function futil.table.pairs_by_value(t, cmp) - cmp = cmp or default_cmp - local s = {} - for k, v in pairs(t) do - table.insert(s, { k, v }) - end - - table.sort(s, function(a, b) - return cmp(a[2], b[2]) - end) - - local i = 0 - return function() - i = i + 1 - local v = s[i] - if v then - return unpack(v) - else - return nil - end - end -end - -function futil.table.pairs_by_key(t, cmp) - cmp = cmp or default_cmp - local s = {} - for k, v in pairs(t) do - table.insert(s, { k, v }) - end - - table.sort(s, function(a, b) - return cmp(a[1], b[1]) - end) - - local i = 0 - return function() - i = i + 1 - local v = s[i] - if v then - return unpack(v) - else - return nil - end - end -end - -function futil.table.size(t) - local size = 0 - for _ in pairs(t) do - size = size + 1 - end - return size -end - -function futil.table.is_empty(t) - return next(t) == nil -end - -function futil.table.count_elements(t) - local counts = {} - for _, item in ipairs(t) do - counts[item] = (counts[item] or 0) + 1 - end - return counts -end - -function futil.table.sets_intersect(set1, set2) - for k in pairs(set1) do - if set2[k] then - return true - end - end - - return false -end - -function futil.table.iterate(t) - local i = 0 - return function() - i = i + 1 - return t[i] - end -end - -function futil.table.reversed(t) - local len = #t - local reversed = {} - - for i = len, 1, -1 do - reversed[len - i + 1] = t[i] - end - - return reversed -end - -function futil.table.contains(t, value) - for _, v in ipairs(t) do - if v == value then - return true - end - end - - return false -end - -function futil.table.keys(t) - local keys = {} - for key in pairs(t) do - keys[#keys + 1] = key - end - return keys -end - -function futil.table.ikeys(t) - local key - return function() - key = next(t, key) - return key - end -end - -function futil.table.values(t) - local values = {} - for _, value in pairs(t) do - values[#values + 1] = value - end - return values -end - -function futil.table.sort_keys(t, cmp) - local keys = futil.table.keys(t) - table.sort(keys, cmp) - return keys -end - --- https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle -function futil.table.shuffle(t, rnd) - rnd = rnd or math.random - for i = #t, 2, -1 do - local j = rnd(i) - t[i], t[j] = t[j], t[i] - end - return t -end - -local function swap(t, i, j) - t[i], t[j] = t[j], t[i] -end - -futil.table.swap = swap - -function futil.table.get(t, key, default) - local value = t[key] - if value == nil then - return default - end - return value -end - -function futil.table.setdefault(t, key, default) - local value = t[key] - if value == nil then - t[key] = default - return default - end - return value -end - -function futil.table.pack(...) - return { n = select("#", ...), ... } -end diff --git a/mods/futil/util/time.lua b/mods/futil/util/time.lua deleted file mode 100644 index ec7de0cb..00000000 --- a/mods/futil/util/time.lua +++ /dev/null @@ -1,29 +0,0 @@ -local idiv = futil.math.idiv - --- convert a number of seconds into a more human-readable value --- ignores the actual passage of time and assumes all years are 365 days -function futil.seconds_to_interval(time) - local s, m, h, d - - time, s = idiv(time, 60) - time, m = idiv(time, 60) - time, h = idiv(time, 24) - time, d = idiv(time, 365) - - if time ~= 0 then - return ("%d years %d days %02d:%02d:%02d"):format(time, d, h, m, s) - elseif d ~= 0 then - return ("%d days %02d:%02d:%02d"):format(d, h, m, s) - elseif h ~= 0 then - return ("%02d:%02d:%02d"):format(h, m, s) - elseif m ~= 0 then - return ("%02d:%02d"):format(m, s) - else - return ("%ds"):format(s) - end -end - --- ISO 8601 date format -function futil.format_utc(timestamp) - return os.date("!%Y-%m-%dT%TZ", timestamp) -end diff --git a/mods/hotbar_switching b/mods/hotbar_switching new file mode 160000 index 00000000..5bb3910b --- /dev/null +++ b/mods/hotbar_switching @@ -0,0 +1 @@ +Subproject commit 5bb3910bd9c1b3cb715bc400dea723c391b82211 diff --git a/mods/hotbar_switching/LICENSE.txt b/mods/hotbar_switching/LICENSE.txt deleted file mode 100644 index d3061a37..00000000 --- a/mods/hotbar_switching/LICENSE.txt +++ /dev/null @@ -1,13 +0,0 @@ -Zero-Clause BSD -============= - -Permission to use, copy, modify, and/or distribute this software for -any purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED ā€œAS ISā€ AND THE AUTHOR DISCLAIMS ALL -WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE -FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY -DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN -AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/mods/hotbar_switching/README.md b/mods/hotbar_switching/README.md deleted file mode 100644 index 698f772a..00000000 --- a/mods/hotbar_switching/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Hotbar Switching - -Implements a way to switch your hotbar to another row. Inspired by [better than adventure](https://www.betterthanadventure.net/) (it has a feature where if you press `tab`, it will change the hotbar like this mod, but doesn't change your inventory). - -## Defaults - requires the `controls` mod -- `sneak` + `aux1` + `left mouse button` - cycle -- `sneak` + `aux1` + `right mouse button` - cycle backwards - -## Mobile - -I believe this would be best done with a custom button, which i think isn't possible in luanti, or there should at least something in the inventory menu. -There are a lot of inventory mods, i think it would be very difficult to support them all. - -## How does it work? - -There is no way to actually decide which slots are part of your hotbar in luanti. So this just shifts items items in your inventory. - -The width of the inventory is assumed to be your hotbar size. - -## Why `controls` mod isn't optional - -ContentDB doesn't allow you to have some sort of "strongly suggested dependancies", so i think it would be best for this mod to work out of the box, instead of not doing anything by default like a library. You may change this in games that use this mod. - -Also i didn't want to reinvent the wheel again. - -## API -- `hotbar_switching` - a table, contains everything -- `hotbar_switching.default_controls = true` - enable default controls -- `hotbar_switching.can_player_switch(player)` - Can the player switch their hotbar to another row - - `player` is an ObjectRef (player) - - returns a boolean, by default always returns `true` - - Override the function if you need to decide -- `hotbar_switching.switch(player, row)` - Actually perform the action of switching the hotbar to that row - - `player` - ObjectRef (player) - - `row` - integer (Usually `-1` or `1`) - - The inventory list being manipulated will be from `player:get_wield_list()` - - You may override this function to change what happens at the end of it (example: sending a message saying you did that action) diff --git a/mods/hotbar_switching/init.lua b/mods/hotbar_switching/init.lua deleted file mode 100644 index 91c37c01..00000000 --- a/mods/hotbar_switching/init.lua +++ /dev/null @@ -1,67 +0,0 @@ ----@diagnostic disable: undefined-global, lowercase-global, unused-local --- any of these functions are designed to be overriden by mods/games - -_G.hotbar_switching = { - default_controls = true, -} - ---- If your mod implements a situation where hotbar switching is not acceptable, you can modify this function ---- do something like: ---- ```lua ---- local old_player_can_switch = hotbar_switching.can_player_switch ---- function hotbar_switching.can_player_switch(player) ---- ---- return hotbar_switching.can_player_switch(player) ---- end ---- ``` ----@param player userdata|table ----@return boolean -function hotbar_switching.can_player_switch(player) - return true -end - ---- You can override to specify what should happen after switching ---- You can use this to make a fancy UI i guess, but that is an excercise for the viewer ----@param player userdata|table ----@param row integer -function hotbar_switching.switch(player, row) - local listname = player:get_wield_list() - local inv = player:get_inventory() - local hotbar_size = player:hud_get_hotbar_itemcount() - local list = inv:get_list(listname) - local list_size = #list - - -- suppose hotbar size is 17 items (why) and inv size was 32 - -- this mod can't really handle that, so just silently fail - if hotbar_size * 2 > list_size then return end - if row < 0 then row = (list_size / hotbar_size) + row end - - local from_index = row * hotbar_size - - local new_list = {} - - for i = 1, list_size do - new_list[i] = list[(i + from_index) % list_size] - end - - inv:set_list(listname, new_list) -end - --- core.get_modpath over core.global_exists in this case because `controls` is a super generic name -if core.get_modpath 'controls' then - controls.register_on_press(function(player, key) - local c = player:get_player_control() - local activated = c.sneak and c.aux1 - if - _G.hotbar_switching.default_controls == true - and hotbar_switching.can_player_switch(player) - and activated - then - if key == 'LMB' then - hotbar_switching.switch(player, 1) - elseif key == 'RMB' then - hotbar_switching.switch(player, -1) - end - end - end) -end diff --git a/mods/hotbar_switching/mod.conf b/mods/hotbar_switching/mod.conf deleted file mode 100644 index 8b83ed51..00000000 --- a/mods/hotbar_switching/mod.conf +++ /dev/null @@ -1,2 +0,0 @@ -name = hotbar_switching -depends = controls diff --git a/mods/libox b/mods/libox new file mode 160000 index 00000000..08d49da4 --- /dev/null +++ b/mods/libox @@ -0,0 +1 @@ +Subproject commit 08d49da483cc56e8852ce9d941a57867e11074d1 diff --git a/mods/libox/.github/workflows/luacheck.yml b/mods/libox/.github/workflows/luacheck.yml deleted file mode 100644 index 26f8e1fb..00000000 --- a/mods/libox/.github/workflows/luacheck.yml +++ /dev/null @@ -1,34 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2024 mt-mods -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - - - -name: luacheck -on: [push, pull_request] -jobs: - luacheck: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@main - - name: Luacheck - uses: lunarmodules/luacheck@master \ No newline at end of file diff --git a/mods/libox/LICENSE.md b/mods/libox/LICENSE.md deleted file mode 100644 index 4be85568..00000000 --- a/mods/libox/LICENSE.md +++ /dev/null @@ -1,170 +0,0 @@ - -pat.lua is licensed under the MIT license, see pat.lua for the license itself - -github/workflows/luacheck.yml is licensed under the MIT license, see the file for the license itself - -path shortening is in utils.lua, licensed under the MIT license, see utils.lua for the license itself - -os.date is in env.lua, the code for that was made by OgelGames (in the mooncontroller project), licensed under LGPLv3 (see below for the copy of the lgplv3 license) - -The LGPLv3 applies to all code (unless a different license is mentioned) in this project. - -============================= - -# GNU LESSER GENERAL PUBLIC LICENSE - -Version 3, 29 June 2007 - -Copyright (C) 2007 Free Software Foundation, Inc. - - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - -This version of the GNU Lesser General Public License incorporates the -terms and conditions of version 3 of the GNU General Public License, -supplemented by the additional permissions listed below. - -## 0. Additional Definitions. - -As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the -GNU General Public License. - -"The Library" refers to a covered work governed by this License, other -than an Application or a Combined Work as defined below. - -An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - -A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - -The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - -The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - -## 1. Exception to Section 3 of the GNU GPL. - -You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - -## 2. Conveying Modified Versions. - -If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - -- a) under this License, provided that you make a good faith effort - to ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or -- b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - -## 3. Object Code Incorporating Material from Library Header Files. - -The object code form of an Application may incorporate material from a -header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - -- a) Give prominent notice with each copy of the object code that - the Library is used in it and that the Library and its use are - covered by this License. -- b) Accompany the object code with a copy of the GNU GPL and this - license document. - -## 4. Combined Works. - -You may convey a Combined Work under terms of your choice that, taken -together, effectively do not restrict modification of the portions of -the Library contained in the Combined Work and reverse engineering for -debugging such modifications, if you also do each of the following: - -- a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. -- b) Accompany the Combined Work with a copy of the GNU GPL and this - license document. -- c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. -- d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of - this License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with - the Library. A suitable mechanism is one that (a) uses at run - time a copy of the Library already present on the user's - computer system, and (b) will operate properly with a modified - version of the Library that is interface-compatible with the - Linked Version. -- e) Provide Installation Information, but only if you would - otherwise be required to provide such information under section 6 - of the GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the Application - with a modified version of the Linked Version. (If you use option - 4d0, the Installation Information must accompany the Minimal - Corresponding Source and Corresponding Application Code. If you - use option 4d1, you must provide the Installation Information in - the manner specified by section 6 of the GNU GPL for conveying - Corresponding Source.) - -## 5. Combined Libraries. - -You may place library facilities that are a work based on the Library -side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - -- a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities, conveyed under the terms of this License. -- b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - -## 6. Revised Versions of the GNU Lesser General Public License. - -The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -as you received it specifies that a certain numbered version of the -GNU Lesser General Public License "or any later version" applies to -it, you have the option of following the terms and conditions either -of that published version or of any later version published by the -Free Software Foundation. If the Library as you received it does not -specify a version number of the GNU Lesser General Public License, you -may choose any version of the GNU Lesser General Public License ever -published by the Free Software Foundation. - -If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/mods/libox/README.md b/mods/libox/README.md deleted file mode 100644 index aa0f0b0a..00000000 --- a/mods/libox/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Libox - -A minetest sandboxing library, offering a basic environment, utilities, normal sandbox and a "coroutine" sandbox - -Everything is avaliable in the async environment except the coroutine sandbox (due to minetest limitations) - -See [api.md](https://github.com/TheEt1234/libox/blob/master/api.md) for documentation and definitions - -See [env_docs.md](https://github.com/TheEt1234/libox/blob/master/env_docs.md) for documentation of the sandbox environment - -# Notice! - -Libox (optionally) requires insecure environment to weigh local variables and upvalues in the coroutine sandbox. **Without this someone can overfill your memory with local variables/upvalues** -***the libox mod will expose debug.getlocal and debug.getupvalue to all mods*** - -Also if you are using coroutine sandboxes, please use luaJIT (default) instead of PUC lua, as there are a lot of differences between them - -# Security - -- *Somewhat* fixes mesecons issue #516 by limiting based on time not instructions -- ***May introduce new bugs, only time can test that*** -- Some of the responsibility is also on the mods that use libox as well (such as not doing something dumb like calling functions straight from the environment), the purpose of libox should be to also handle some of the more common stuff - -# Optional dependancies -dbg - not actually used for debugging, just used to provide `dbg.shorten_path`, if unavaliable it will fallback to the copied implementation - -# Credits - see License.md for the actual licenses - -Code (unless mentioned somewhere differently) - LGPLv3 - -`libox.shorten_path` - [The minetest dbg mod's shorten_path.lua](https://github.com/appgurueu/dbg/blob/master/src/shorten_path.lua) - MIT licensed - -`pat.lua`: [source](https://notabug.org/pgimeno/patlua/src/master/pat.lua) [and the mesecons issue](https://github.com/minetest-mods/mesecons/issues/456) - MIT licensed - -`github/workflows/luacheck.yml` - from mt-mods, [original source here ](https://github.com/mt-mods/mt-mods/blob/master/snippets/luacheck.yml) - MIT licensed - -`os.date` in the environment - from mooncontroller [original repo here](https://github.com/mt-mods/mooncontroller) - -Inspiration: [Luacontrollers](https://github.com/minetest-mods/mesecons/tree/master/mesecons_luacontroller) \ No newline at end of file diff --git a/mods/libox/api.md b/mods/libox/api.md deleted file mode 100644 index 7c1d0a48..00000000 --- a/mods/libox/api.md +++ /dev/null @@ -1,150 +0,0 @@ -# Api docs - -## Disclaimers -- ***DO NOT EVER CALL FUNCTIONS FROM THE ENVIRONMENT (once the environment is defined) AND DO NOT CALL ANY FUNCTIONS RETURNED BY THE SANDBOX*** that will just bypass the debug hook and allow someone to `repeat until false` and *stop* the server that way -- anything coming out of the sandbox should *not* be called, and be checked for every single detail (like you are writing a digiline device) - -## Definitions -- microsecond: milisecond * 1000 -- hook: a lua function that runs every `n` instructions -- environment: The values that the sandboxed code can work with -- string sandbox: when the `__index` field of a string's metatable gets replaced with the sandboxes `string` (basically, a thing to prevent the sandbox from using unsafe string functions through `"":unsafe_function()`) - -## Differences from luac-like sandboxing -- The basic environment is much more permissive (but still maintains safety) -- You get limited not by instructions (luacontroller calls them events for some reason??? even if it calls *triggers* events too??? this is so dumb) but by time *by default* -- You get a traceback *by default* - -## Utilities -`libox.get_default_hook(max_time)` - Get the hook function that will terminate the program in `max_time` microseconds - -`libox.traceback(...)` - a function that gives a friendlier traceback, now safe to expose to sandbox - -`msg, cost = libox.digiline_sanitize(input, allow_functions, wrap)` - use this instead of your own clean_and_weigh_digiline_message implementation, `wrap` is a function that accepts a function and returns another one, this gets called on user functions - -`libox.sandbox_lib_f(f, ...)` - use this if you want to escape the string sandbox (do this if you are not 100% sure that your code is free of `"":this_stuff()`) **don't use this on functions that run user - -`libox.disabled = true/false` - use when you want to disable libox (e.g. when using jitprofiler) - -`libox.has_autohook = true/false` - Is autohook available or not? - -**functions** - -`libox.type_check(thing, check)` -- `thing` = untrusted data -- `check` a function or a table with the following format: -```lua - { - something = function(value) return true end, - a_table = { - other_key = libox.type("number"), - x = libox.type_vector, -- this is how type_vector is supposed to be used, don't do libox.type_vector() in this case - } - } -``` -- runs a series of checks on the `thing` using the check table -- if the `thing` table contains an extra property, not defined in the `check` table, it will return false too -- if the `thing` table lacks a property in the `check` table, it will return false -- returns a boolean, and a string (success, faulty element) - -`libox.type(type)` - returns a function that checks a type -- for example, when given `"number"`, it returns a function: `function(x) return type(x) == "number" end` - -`libox.type_vector(x)` - is a function that checks if `x` is a vector, use `vector.check` if you want to also check the vector metatable - -`libox.shorten_path(path)` - use this to shorten a path, it will convert `/home/user/blabla/.minetest/modname/x.lua` into `modname:x.lua` - if the `dbg` mod is avaliable, it will simply use `dbg.shorten_path` -## Environment -`libox.create_basic_environment()` - get a basic secure environment already set up for you - -`libox.supply_additional_environment(env)` - a function that lets other mods extend the environment, gets called after environment creation, by default is `function(...) return ... end` - -`libox.safe.*` - safe functions/classes, used in libox.create_basic_environment, used internally, you shouldn't modify this table - -## "Normal" sandbox - -`libox.normal_sandbox(def)` -- A sandbox that executes lua code securely based on parameters in `def` (table) -### The def table -- `def.code` - the code... -- `def.env` - The environment of the function -- `def.error_handler` - A function inside the `xpcall`, by default `libox.traceback` -- `def.in_hook` - The hook function, by default `libox.get_default_hook(def.max_time)` -- `def.max_time` - Maximum allowed execution time, in microseconds, only used if `def.in_hook` was not defined -- `def.hook_time` - The hook function will execute every `def.hook_time` instructions, by default 50 -- `def.function_wrap` - transforms a function, by default `function(f) return f end` - -## "Coroutine" sandbox -- Optionally requires trusted environment for weighing local variables and upvalues - - without it someone can overfill your memory, but libox has protections against that *somewhat, though i don't think it's a good idea to rely on them* - -### What is it? -A sandbox that allows the user to **yield** => temporarily stop execution; then be able to resume from that point - -### garbage collection -`libox.coroutine.settings` -- memory_treshold: in gigabytes, if lua's memory reaches above this limit, the hook will error, the user is meant to configure this to their needs *also this is what i meant about those overfill protections, not exactly reliable* -- gc settings: - - time_treshold: if a sandbox has been untouched for this long, collect it, in seconds - - number_of_sandboxes: the garbage collection will trigger if the number of stored sandboxes is above this limit - - auto: if true, garbage collection will automatically activate, i don't think this is nessesary if you have trusted the libox mod - - interval: in seconds, when to trigger the garbage collection - -All of theese are configurable by the user - -`libox.coroutine.garbage_collect()` - trigger the garbage collection - -### the docs -- When libox is a trusted mod, it exposes `debug.getlocal` and `debug.getupvalue` - -`libox.coroutine.active_sandboxes` - A table containing all the active sandboxes, where the key is the sandbox's id, and the value is the sandbox definition and thread - -`libox.coroutine.create_sandbox(def)` -- returns an ID to the sandbox (can be used in libox.coroutine.* functions or just be able to see the sandbox yourself with `libox.coroutine.active_sandboxes[id]`) - -- `def.ID` - A custom id, by default random text -- `def.code` - the code -- `def.is_garbage_collected` - if this sandbox should be garbage collected, by default true -- `def.env` - the environment, by default a blank table -- `def.in_hook` - hook builder function, by default `libox.coroutine.get_default_hook(def.time_limit or libox.default_time_limit)` or `nil` if autohook is available+enabled. See `libox.coroutine.get_default_hook` on how to use, it is different to how normal sandbox handles it. -- `def.time_limit` - used if `debug.in_hook` is not avaliable, by default 3000 -- `last_ran` - not set by you, but is the last time the sandbox was ran, used for garbage collection -- `def.hook_time` - The hook function will execute every `def.hook_time` instructions, by default 10 -- `def.size_limit` - in *bytes*, the size limit of the sandbox, if trusted then upvalues and local variables are counted in too, by default 5 *megabytes*, aka `1024*1024*5` bytes -- `def.function_wrap` - transforms a function, by default `function(f) return f end` -- `def.autohook` - enables/disables auto-yielding inside sandboxes, respecting `def.hook_time` and `def.time_limit`. `def.in_hook` behaves a bit differently. Please read `autohook/README.md` document for more info. By default, this is disabled (`false`). - -`libox.coroutine.get_default_hook(max_time)` - returns a hook builder function. The created hook performs a time limit check whether to raise an error or not. Raising an error kills the sandbox. - -`libox.coroutine.run_sandbox(ID, value_passed)` -- `value_passed` - the value passed to the coroutine.resume function, so that in the sandbox it could: `local vals = coroutine.yield("blabla")` -- Returns ok, errmsg_or_value (the `"blabla"` in `coroutine.yield("blabla")`) - -`libox.coroutine.size_check(env, lim, thread)` -- `env` - environment of the thread -- `lim` - the limit -- `thread` - the thread -- returns if its size (computed using `get_size`) is less than the lim -- used internally - -`libox.coroutine.get_size(env, seen, thread, recursed)` -- get the size in bytes of a thread, used by size_check -- normal usage: `libox.coroutine.get_size(env, {}, thread, false)` - -`libox.coroutine.is_sandbox_dead(id)` -- detects if the sandbox is dead - - -# Async -- everything else other than the coroutine sandbox is avaliable in both sync and async environments - -coroutine sandbox is not avaliable in async because - -1) I cannot import the debug.getlocal and debug.getupvalue functions into the async environment -2) I cannot import a coroutine in the async environment - -# Examples -- [libox controller](https://github.com/TheEt1234/libox_controller) - -# Todos -- proper examples -- ~~Maybe automatic yielding? depends on how possible that is~~ it's not really... \ No newline at end of file diff --git a/mods/libox/autohook/.gitignore b/mods/libox/autohook/.gitignore deleted file mode 100644 index 7b42cc7b..00000000 --- a/mods/libox/autohook/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.xmake -build -compile_commands.json -autohook.h -*.dll -*.so \ No newline at end of file diff --git a/mods/libox/autohook/.xmake/linux/x86_64/cache/config b/mods/libox/autohook/.xmake/linux/x86_64/cache/config deleted file mode 100644 index 44311624..00000000 --- a/mods/libox/autohook/.xmake/linux/x86_64/cache/config +++ /dev/null @@ -1,9 +0,0 @@ -{ - recheck = false, - options = { - clean = true - }, - mtimes = { - ["xmake.lua"] = 1748874796 - } -} \ No newline at end of file diff --git a/mods/libox/autohook/.xmake/linux/x86_64/cache/cxxmodules b/mods/libox/autohook/.xmake/linux/x86_64/cache/cxxmodules deleted file mode 100644 index 6f31cf5a..00000000 --- a/mods/libox/autohook/.xmake/linux/x86_64/cache/cxxmodules +++ /dev/null @@ -1 +0,0 @@ -{ } \ No newline at end of file diff --git a/mods/libox/autohook/.xmake/linux/x86_64/cache/detect b/mods/libox/autohook/.xmake/linux/x86_64/cache/detect deleted file mode 100644 index ed9250f6..00000000 --- a/mods/libox/autohook/.xmake/linux/x86_64/cache/detect +++ /dev/null @@ -1,355 +0,0 @@ -{ - find_program_gcc_arch_x86_64_plat_linux_checktoolld = { - ["g++"] = "/usr/bin/g++" - }, - find_programver = { - ["/usr/bin/gcc"] = "15.1.1", - ["/usr/bin/g++"] = "15.1.1" - }, - find_program = { - gzip = "/usr/bin/gzip", - git = "/usr/bin/git", - emerge = false, - tar = "/usr/bin/tar", - gcc = "/usr/bin/gcc", - sh = "/usr/bin/sh", - pacman = "/usr/bin/pacman", - brew = false, - ["/usr/bin/gcc"] = "/usr/bin/gcc", - ["/usr/bin/g++"] = "/usr/bin/g++", - nim = false, - ["pkg-config"] = "/usr/bin/pkg-config" - }, - ["core.tools.gcc.has_cflags"] = { - ["/usr/bin/gcc_15.1.1"] = { - ["-print-multi-directory"] = true, - ["-print-sysroot"] = true, - ["-save-temps"] = true, - ["-pass-exit-codes"] = true, - ["-o"] = true, - ["-print-search-dirs"] = true, - ["--help"] = true, - ["-S"] = true, - ["-print-multi-lib"] = true, - ["-time"] = true, - ["-dumpmachine"] = true, - ["--param"] = true, - ["-no-canonical-prefixes"] = true, - ["-x"] = true, - ["-Xassembler"] = true, - ["-dumpversion"] = true, - ["-print-multiarch"] = true, - ["-pie"] = true, - ["--version"] = true, - ["-c"] = true, - ["-print-multi-os-directory"] = true, - ["-print-libgcc-file-name"] = true, - ["-dumpspecs"] = true, - ["--target-help"] = true, - ["-Xlinker"] = true, - ["-E"] = true, - ["-B"] = true, - ["-v"] = true, - ["-Xpreprocessor"] = true, - ["-shared"] = true, - ["-pipe"] = true, - ["-print-sysroot-headers-suffix"] = true - } - }, - find_package_linux_x86_64_fetch_package_system = { - luajit_aaeb029456e54f29b904a191c96508d2_release_external = { - sysincludedirs = { - "/usr/include/luajit-2.1" - }, - shared = true, - version = "2.1.1741730670", - libfiles = { - "/usr/lib/libluajit-5.1.so" - }, - links = { - "luajit-5.1" - } - } - }, - ["detect.sdks.find_vcpkgdir"] = false, - find_program_gcc_arch_x86_64_plat_linux_checktoolsh = { - ["g++"] = "/usr/bin/g++" - }, - ["core.tools.gcc.has_ldflags"] = { - ["/usr/bin/g++_15.1.1"] = { - ["--gc-sections"] = true, - ["--major-image-version"] = true, - ["--map-whole-files"] = true, - ["--wrap"] = true, - ["--no-accept-unknown-input-arch"] = true, - ["--relax"] = true, - ["--no-relax"] = true, - ["--warn-rwx-segments"] = true, - ["--gc-keep-exported"] = true, - ["--pop-state"] = true, - ["--no-export-dynamic"] = true, - ["-G"] = true, - ["--disable-large-address-aware"] = true, - ["-nostdlib"] = true, - ["--verbose"] = true, - ["--enable-auto-image-base"] = true, - ["--section-start"] = true, - ["--retain-symbols-file"] = true, - ["--stack"] = true, - ["-Qy"] = true, - ["-Trodata-segment"] = true, - ["--warn-alternate-em"] = true, - ["--export-dynamic-symbol"] = true, - ["--add-stdcall-alias"] = true, - ["--out-implib"] = true, - ["-l"] = true, - ["-Bshareable"] = true, - ["-debug"] = true, - ["--reduce-memory-overheads"] = true, - ["--print-gc-sections"] = true, - ["--error-rwx-segments"] = true, - ["--just-symbols"] = true, - ["--split-by-file"] = true, - ["--trace-symbol"] = true, - ["--task-link"] = true, - ["--strip-all"] = true, - ["-plugin-opt"] = true, - ["-O"] = true, - ["--error-execstack"] = true, - ["--enable-reloc-section"] = true, - ["--script"] = true, - ["--undefined-version"] = true, - ["--no-warnings"] = true, - ["--rosegment"] = true, - ["--exclude-symbols"] = true, - ["--ld-generated-unwind-info"] = true, - ["-dT"] = true, - ["-L"] = true, - ["--no-dynamic-linker"] = true, - ["--format"] = true, - ["-e"] = true, - ["--no-fatal-warnings"] = true, - ["--no-error-rwx-segments"] = true, - ["--defsym"] = true, - ["--disable-stdcall-fixup"] = true, - ["--demangle"] = true, - ["--traditional-format"] = true, - ["--trace"] = true, - ["--discard-all"] = true, - ["--disable-reloc-section"] = true, - ["--version-script"] = true, - ["-Bgroup"] = true, - ["--strip-discarded"] = true, - ["--subsystem"] = true, - ["--strip-debug"] = true, - ["--no-eh-frame-hdr"] = true, - ["-Bno-symbolic"] = true, - ["--warn-textrel"] = true, - ["--filter"] = true, - ["--dependency-file"] = true, - ["--enable-auto-import"] = true, - ["-A"] = true, - ["--default-symver"] = true, - ["-I"] = true, - ["--no-warn-rwx-segments"] = true, - ["--warn-common"] = true, - ["--push-state"] = true, - ["-assert"] = true, - ["--minor-image-version"] = true, - ["--support-old-code"] = true, - ["--export-dynamic"] = true, - ["--library"] = true, - ["--no-allow-shlib-undefined"] = true, - ["--discard-locals"] = true, - ["-f"] = true, - ["--no-demangle"] = true, - ["-Map"] = true, - ["--exclude-modules-for-implib"] = true, - ["--fatal-warnings"] = true, - ["-fini"] = true, - ["--whole-archive"] = true, - ["--no-define-common"] = true, - ["-T"] = true, - ["-Y"] = true, - ["--omagic"] = true, - ["--default-script"] = true, - ["--error-unresolved-symbols"] = true, - ["-EB"] = true, - ["--spare-dynamic-tags"] = true, - ["--dynamic-list-data"] = true, - ["-no-pie"] = true, - ["-Ttext"] = true, - ["--no-strip-discarded"] = true, - ["--no-print-map-locals"] = true, - ["--no-ctf-variables"] = true, - ["--force-exe-suffix"] = true, - ["-rpath-link"] = true, - ["-plugin-save-temps"] = true, - ["-F"] = true, - ["--major-subsystem-version"] = true, - ["--large-address-aware"] = true, - ["--enable-non-contiguous-regions-warnings"] = true, - ["-plugin"] = true, - ["-soname"] = true, - ["--allow-shlib-undefined"] = true, - ["--oformat"] = true, - ["--no-whole-archive"] = true, - ["--relocatable"] = true, - ["-u"] = true, - ["--remap-inputs"] = true, - ["--enable-linker-version"] = true, - ["--auxiliary"] = true, - ["--disable-auto-image-base"] = true, - ["-Tdata"] = true, - ["-Tldata-segment"] = true, - ["--accept-unknown-input-arch"] = true, - ["--no-warn-mismatch"] = true, - ["--no-print-map-discarded"] = true, - ["--print-output-format"] = true, - ["-z"] = true, - ["-dp"] = true, - ["--enable-extra-pep-debug"] = true, - ["--as-needed"] = true, - ["--end-group"] = true, - ["--sort-section"] = true, - ["--copy-dt-needed-entries"] = true, - ["-P"] = true, - ["--file-alignment"] = true, - ["--no-print-gc-sections"] = true, - ["--cref"] = true, - ["-V"] = true, - ["--version-exports-section"] = true, - ["-EL"] = true, - ["-g"] = true, - ["--no-warn-search-mismatch"] = true, - ["--no-warn-execstack"] = true, - ["-h"] = true, - ["--enable-non-contiguous-regions"] = true, - ["--dynamic-list-cpp-new"] = true, - ["--architecture"] = true, - ["--no-check-sections"] = true, - ["--dynamic-list-cpp-typeinfo"] = true, - ["--section-ordering-file"] = true, - ["--disable-long-section-names"] = true, - ["--disable-linker-version"] = true, - ["--check-sections"] = true, - ["--output"] = true, - ["--disable-auto-import"] = true, - ["--no-ld-generated-unwind-info"] = true, - ["--help"] = true, - ["--ignore-unresolved-symbol"] = true, - ["-o"] = true, - ["--export-all-symbols"] = true, - ["--nmagic"] = true, - ["--sort-common"] = true, - ["--print-map-discarded"] = true, - ["-R"] = true, - ["--image-base"] = true, - ["--no-gc-sections"] = true, - ["--warn-multiple-gp"] = true, - ["-Bsymbolic-functions"] = true, - ["--version"] = true, - ["-c"] = true, - ["--default-imported-symver"] = true, - ["--library-path"] = true, - ["--warn-execstack-objects"] = true, - ["-static"] = true, - ["-Ttext-segment"] = true, - ["--warn-section-align"] = true, - ["--eh-frame-hdr"] = true, - ["--start-group"] = true, - ["--split-by-reloc"] = true, - ["--print-sysroot"] = true, - ["--disable-new-dtags"] = true, - ["--enable-stdcall-fixup"] = true, - ["--exclude-all-symbols"] = true, - ["--warn-duplicate-exports"] = true, - ["--orphan-handling"] = true, - ["--require-defined"] = true, - ["--gpsize"] = true, - ["--error-handling-script"] = true, - ["--undefined"] = true, - ["--remap-inputs-file"] = true, - ["--print-map"] = true, - ["--no-as-needed"] = true, - ["-Tbss"] = true, - ["--no-copy-dt-needed-entries"] = true, - ["--enable-long-section-names"] = true, - ["--minor-subsystem-version"] = true, - ["--entry"] = true, - ["-Ur"] = true, - ["--kill-at"] = true, - ["--dll"] = true, - ["-b"] = true, - ["--warn-execstack"] = true, - ["--print-memory-usage"] = true, - ["-qmagic"] = true, - ["--print-map-locals"] = true, - ["--enable-extra-pe-debug"] = true, - ["--output-def"] = true, - ["--no-undefined-version"] = true, - ["--unique"] = true, - ["-init"] = true, - ["--dynamic-linker"] = true, - ["--ctf-variables"] = true, - ["--heap"] = true, - ["--allow-multiple-definition"] = true, - ["--warn-once"] = true, - ["--enable-runtime-pseudo-reloc"] = true, - ["--force-group-allocation"] = true, - ["--major-os-version"] = true, - ["--compat-implib"] = true, - ["--export-dynamic-symbol-list"] = true, - ["--stats"] = true, - ["-rpath"] = true, - ["-a"] = true, - ["--section-alignment"] = true, - ["--no-error-execstack"] = true, - ["--discard-none"] = true, - ["-y"] = true, - ["--no-rosegment"] = true, - ["--no-undefined"] = true, - ["--no-keep-memory"] = true, - ["-Bsymbolic"] = true, - ["--disable-multiple-abs-defs"] = true, - ["--pic-executable"] = true, - ["--no-omagic"] = true, - ["--emit-relocs"] = true, - ["--enable-new-dtags"] = true, - ["--disable-runtime-pseudo-reloc"] = true, - ["--minor-os-version"] = true, - ["--target-help"] = true, - ["--dynamic-list"] = true, - ["--exclude-libs"] = true, - ["--warn-unresolved-symbols"] = true, - ["--mri-script"] = true, - ["--no-map-whole-files"] = true, - ["-flto"] = true, - ["-m"] = true - } - }, - find_program_gcc_arch_x86_64_plat_linux_checktoolcc = { - gcc = "/usr/bin/gcc" - }, - ["lib.detect.has_flags"] = { - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wno-newline-eof"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wno-unused-parameter"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wextra"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc__-m64_-fPIC"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wno-declaration-after-statement"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wpedantic"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wno-unused-function"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wno-missing-prototypes"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cxflags_-m64_-fdiagnostics-color=always"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wno-switch-default"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cxflags_-m64_-MMD -MF"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cxflags_-m64_-Wno-gnu-line-marker -Werror"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wall"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wno-unused-macros"] = true, - ["linux_x86_64_/usr/bin/g++_15.1.1_sh__-shared -m64 -m64_-fPIC"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wno-gnu-zero-variadic-macro-arguments"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wno-unsafe-buffer-usage"] = true, - ["linux_x86_64_/usr/bin/g++_15.1.1_ld__-m64 -m64_-fPIC"] = true, - ["linux_x86_64_/usr/bin/gcc_15.1.1_cc_cflags_-m64_-Wno-extra-semi-stmt"] = true - } -} \ No newline at end of file diff --git a/mods/libox/autohook/.xmake/linux/x86_64/cache/history b/mods/libox/autohook/.xmake/linux/x86_64/cache/history deleted file mode 100644 index c886198c..00000000 --- a/mods/libox/autohook/.xmake/linux/x86_64/cache/history +++ /dev/null @@ -1,19 +0,0 @@ -{ - cmdlines = { - "xmake lua /usr/share/xmake/modules/private/utils/statistics.lua", - "xmake config -c", - "xmake config -c", - "xmake config -c -vD", - "xmake config -c -vD", - "xmake config -c -vD", - "xmake config -c -vD", - "xmake config -c -vD", - "xmake config -c -vD", - "xmake config -c -vD", - "xmake config -c -vD", - "xmake config -c -vD", - "xmake config -c", - "xmake ", - "xmake lua /usr/share/xmake/actions/build/cleaner.lua" - } -} \ No newline at end of file diff --git a/mods/libox/autohook/.xmake/linux/x86_64/cache/option b/mods/libox/autohook/.xmake/linux/x86_64/cache/option deleted file mode 100644 index 6f31cf5a..00000000 --- a/mods/libox/autohook/.xmake/linux/x86_64/cache/option +++ /dev/null @@ -1 +0,0 @@ -{ } \ No newline at end of file diff --git a/mods/libox/autohook/.xmake/linux/x86_64/cache/package b/mods/libox/autohook/.xmake/linux/x86_64/cache/package deleted file mode 100644 index 520c6268..00000000 --- a/mods/libox/autohook/.xmake/linux/x86_64/cache/package +++ /dev/null @@ -1,23 +0,0 @@ -{ - luajit = { - __requirestr = "luajit", - shared = true, - envs = { - PATH = { - "/home/et/.xmake/packages/l/luajit/v2.1.0-beta3/aaeb029456e54f29b904a191c96508d2/bin" - } - }, - __requireconfs = { - optional = true, - configs = { - shared = false - }, - system = true - }, - __enabled = true, - libfiles = "/usr/lib/libluajit-5.1.so", - sysincludedirs = "/usr/include/luajit-2.1", - version = "2.1.1741730670", - links = "luajit-5.1" - } -} \ No newline at end of file diff --git a/mods/libox/autohook/.xmake/linux/x86_64/cache/project b/mods/libox/autohook/.xmake/linux/x86_64/cache/project deleted file mode 100644 index 6f31cf5a..00000000 --- a/mods/libox/autohook/.xmake/linux/x86_64/cache/project +++ /dev/null @@ -1 +0,0 @@ -{ } \ No newline at end of file diff --git a/mods/libox/autohook/.xmake/linux/x86_64/cache/references b/mods/libox/autohook/.xmake/linux/x86_64/cache/references deleted file mode 100644 index eb6e941c..00000000 --- a/mods/libox/autohook/.xmake/linux/x86_64/cache/references +++ /dev/null @@ -1,3 +0,0 @@ -{ - packages = { } -} \ No newline at end of file diff --git a/mods/libox/autohook/.xmake/linux/x86_64/cache/repository b/mods/libox/autohook/.xmake/linux/x86_64/cache/repository deleted file mode 100644 index 06504c14..00000000 --- a/mods/libox/autohook/.xmake/linux/x86_64/cache/repository +++ /dev/null @@ -1,12 +0,0 @@ -{ - artifacts_urls = { - "https://gitlab.com/xmake-mirror/build-artifacts.git", - "https://github.com/xmake-mirror/build-artifacts.git", - "https://gitee.com/xmake-mirror/build-artifacts.git" - }, - mainurls = { - "https://gitlab.com/tboox/xmake-repo.git", - "https://github.com/xmake-io/xmake-repo.git", - "https://gitee.com/tboox/xmake-repo.git" - } -} \ No newline at end of file diff --git a/mods/libox/autohook/.xmake/linux/x86_64/cache/toolchain b/mods/libox/autohook/.xmake/linux/x86_64/cache/toolchain deleted file mode 100644 index 0fa5bfa3..00000000 --- a/mods/libox/autohook/.xmake/linux/x86_64/cache/toolchain +++ /dev/null @@ -1,133 +0,0 @@ -{ - gfortran_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = true - }, - envs_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = true - }, - fpc_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = true - }, - tool_target_autohook_linux_x86_64_sh = { - toolchain_info = { - name = "gcc", - plat = "linux", - arch = "x86_64", - cachekey = "gcc_arch_x86_64_plat_linux" - }, - toolname = "gxx", - program = "/usr/bin/g++" - }, - yasm_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = true - }, - tool_platform_linux_x86_64_ld = { - toolchain_info = { - name = "gcc", - cachekey = "gcc_arch_x86_64_plat_linux", - arch = "x86_64", - plat = "linux" - }, - toolname = "gxx", - program = "/usr/bin/g++" - }, - cuda_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = true - }, - nasm_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = true - }, - nim_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = false - }, - swift_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = true - }, - go_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = true - }, - tool_platform_linux_x86_64_cc = { - toolchain_info = { - name = "gcc", - cachekey = "gcc_arch_x86_64_plat_linux", - arch = "x86_64", - plat = "linux" - }, - toolname = "gcc", - program = "/usr/bin/gcc" - }, - gcc_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = { - name = "gcc", - program = "/usr/bin/gcc" - } - }, - cross_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = false - }, - fasm_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = true - }, - tool_target_autohook_linux_x86_64_ld = { - toolchain_info = { - name = "gcc", - plat = "linux", - arch = "x86_64", - cachekey = "gcc_arch_x86_64_plat_linux" - }, - toolname = "gxx", - program = "/usr/bin/g++" - }, - tool_target_autohook_linux_x86_64_cc = { - toolchain_info = { - name = "gcc", - plat = "linux", - arch = "x86_64", - cachekey = "gcc_arch_x86_64_plat_linux" - }, - toolname = "gcc", - program = "/usr/bin/gcc" - }, - rust_arch_x86_64_plat_linux = { - plat = "linux", - arch = "x86_64", - __global = true, - __checked = true - } -} \ No newline at end of file diff --git a/mods/libox/autohook/.xmake/linux/x86_64/project.lock b/mods/libox/autohook/.xmake/linux/x86_64/project.lock deleted file mode 100644 index e69de29b..00000000 diff --git a/mods/libox/autohook/.xmake/linux/x86_64/xmake.conf b/mods/libox/autohook/.xmake/linux/x86_64/xmake.conf deleted file mode 100644 index 949bcba1..00000000 --- a/mods/libox/autohook/.xmake/linux/x86_64/xmake.conf +++ /dev/null @@ -1,26 +0,0 @@ -{ - arch = "x86_64", - buildir = "build", - ccache = true, - clean = true, - host = "linux", - Iluajit = false, - kind = "static", - Lluajit = false, - mode = "release", - ndk_stdcxx = true, - plat = "linux", - __toolchains_linux_x86_64 = { - "envs", - "gcc", - "yasm", - "nasm", - "fasm", - "cuda", - "go", - "rust", - "swift", - "gfortran", - "fpc" - } -} \ No newline at end of file diff --git a/mods/libox/autohook/README.md b/mods/libox/autohook/README.md deleted file mode 100644 index f28620ed..00000000 --- a/mods/libox/autohook/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# Autohook -Autohook is a feature that allows coroutine sandboxes to automatically yield once they've reached their limits, controlled by `def.time_limit` in coroutine sandbox spec. The way this works is by hooking to the Lua runtime through its C API in order to bypass `yield across C-call boundary` limitation by yielding in C instead of Lua. Because of this, the C module is a bit system-dependent and may require knowledge in C and the general build process to troubleshoot it. - -To enable this feature, game and mod developers need to do a couple of things: - -1. Enable (`def.autohook = true`) when creating your sandbox. It's disabled by default. -2. Ask players to put libox in trusted mods list. An error message will be logged if libox found the C module but encountered problems loading it. -3. Ask players to compile the `libautohook.so/.dll` linking with the same LuaJIT library used with Luanti. Players, please read further below on how to compile it. - -If any of the conditions needed for autohook to work is unfulfilled, autohook is NOT available. As such, libox will safely fallback to regular coroutine sandbox behaviour where manual `yield()` is required. Check `libox.has_autohook`. - -# C Hook behaviour -The autohook uses `def.hook_time` in a normal way to set the C hook, being instruction counts needed to trigger the hook. The default autohook checks if the sandbox has elapsed `def.time_limit` and yields it. It is the auto-yielding form of `libox.coroutine.get_default_hook(def.time_limit or libox.default_time_limit)`. - -If `def.in_hook` is a function, then the C hook will call it instead of its default behaviour. Whenever `def.in_hook` throws an error, the C hook will yield. To kill the sandbox from `def.in_hook`, start your error message with `SANDBOX KILL`. This means that by default, `def.in_hook` unaware of the kill command will always yield. Please update `def.in_hook` accordingly when you enable autohook. - -# Compile the autohook C module -## Compile: Linux/Unix-like systems -1. Get [xmake](https://xmake.io/) either through its installer script or through your package manager. -2. Install LuaJIT. You may install LuaJIT using your system's package manager, or with [xrepo](https://xrepo.xmake.io/). -3. Go to this directory: `libox/autohook/` (the directory where this file is in) -4. `xmake config -c && xmake` - - `--Iluajit=...` is an `xmake config` option specifying where to find LuaJIT header files - - `--Lluajit=...` is an `xmake config` option specifying where to find LuaJIT libraries -5. Done! You can clean up by deleting these directories and files: `rm -r .xmake build` - -## Compile: Windows -The official release of luanti on Windows is cross-compiled from [Ubuntu LTS 22.04](https://releases.ubuntu.com/jammy/) through the [LLVM-MinGW toolchain with Clang under Window's Universal C Runtime](https://github.com/mstorsjo/llvm-mingw/) linked with [pre-compiled libraries by the Luanti team](https://github.com/luanti-org/luanti/blob/5.12.0/util/buildbot/buildwin64.sh#31). This makes it difficult and hard to translate the CMake build scripts for autohook's C module. Currently, no one has yet to successfully build a working autohook DLL with the same approach, hence why `libautohook.dll` isn't vendored directly for Windows. A PRs to fix this is welcome. Please compare with [prior attempt by Acorp/corpserot](https://github.com/corpserot/luanti-win-clang64). - -For now, we'll be going with a simpler setup on Windows natively. You will build luanti from scratch and playing on that build instead of the official release. This is a bit complicated but should be easy to follow. There's a shortcut: Use (and trust) [ACorp's/corpserot's luanti build](https://github.com/corpserot/luanti-win-clang64) which includes a pre-built `libautohook.dll`. Of course, it's always more secure to compile `libautohook.dll` on your own. - -1. Download the source code for the Luanti version you want to play on. For example, click the **Source code** (zip or tar.gz) asset in the [github release page for version 5.12.0](https://github.com/luanti-org/luanti/releases/tag/5.12.0). Extract it. - -2. Follow instructions in the official documentation for engine devs to [compile Luanti on Windows using MSYS2 + Clang64 environment](https://docs.luanti.org/for-engine-devs/compiling/windows/). Don't use the MSVC + vcpkg approach, it's not really well-maintained as other methods. (e.g. using a really, really years old LuaJIT) - - **WARNING**: the typical size of the tools installed is ~2GB. - -3. With luanti compiled, it's recommended you bundle your DLLs since right now it will only run in the Clang64 environment. Follow the [instructions to bundle the DLLs](https://docs.luanti.org/for-engine-devs/compiling/windows/#bundling-dlls) in the same document. You'll be using [rollerozxa's bundledll bash script](https://github.com/rollerozxa/msys2-bundledlls) - -4. Install xmake (and LuaJIT if you haven't): `pacman -Syu mingw-w64-clang-x86_64-luajit mingw-w64-clang-x86_64-xmake`. - -5. Build the C module by running `xmake config --toolchain=clang -c && xmake`. For more advanced needs/wants, here are extra options you can use: - - `--Iluajit=...` is an `xmake config` option specifying where to find LuaJIT header files - - `--Lluajit=...` is an `xmake config` option specifying where to find LuaJIT libraries - -6. Done! You may even uninstall MSYS2 if you like, but I suggest keeping it for future Luanti versions. You can also clean up by deleting these directories and files: `rm -r .xmake build*` - -# For developers -## C module versioning -`module-version.txt` tracks the current C source hash, so always re-build the C module -for any change. OR if you can't (or too lazy) do that, just do something like: - -```sh -cat autohook.c utils.c | sha256sum | awk '{ print $1 }' >module-version.txt -``` - -or on powershell: - -```pwsh -pwsh hash.ps1 autohook.c utils.c > module-version.txt -``` - -## Debug builds -On release builds, the C module disables its own logging capabilities. Logging -occurs through `core.log`, bypassing the sandbox. Logging capabilities is -available in debug builds. So, if you want to log things from the autohook, you -should create a debug build: - -```sh -xmake config -m debug -c && xmake -``` - -## All diagnostics when building -By default, xmake cuts off the diagnostics to a certain number of lines. To see -all of them: - -```sh -xmake config -c && xmake -D -``` - -## Verbose xmake configure and building -In order to debug and inspect what might go wrong in the xmake configuration and -build steps, you can use flags like so: - -```sh -xmake config -c -vD && xmake -vD -``` - -It is very noisy, so perhaps you should each step one-by-one. - -## Obtaining `compile_commands.json` -Pretty much all C/C++ LSPs/IDEs make use of this in order to know all the -required C source code, headers, library, etc. needed to build your target. It's -how any build system can "communicate" with your LSP. You can generate one by running: - -```sh -xmake project -k compile_commands -``` \ No newline at end of file diff --git a/mods/libox/autohook/autohook.c b/mods/libox/autohook/autohook.c deleted file mode 100644 index 5a54750c..00000000 --- a/mods/libox/autohook/autohook.c +++ /dev/null @@ -1,191 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "autohook.h" -#include "utils.c" - -// module-version.txt tracks the current C source hash, so always re-build the C module -// for any change. OR if you can't (or too lazy) do that, just do something like: -// cat autohook.c utils.c | sha256sum | awk '{ print $1 }' >module-version.txt -// or on powershell: hash.ps1 autohook.c utils.c > module-version.txt - -static double prev_time = 0; -static double get_time_ms(void) { - // On Windows, clock_t is 32-bit, meaning after 24 days and 20 hours it - // becomes UB. So uh... don't run your server for longer than that. - ACorp - clock_t now = clock(); - return ((double)now) / CLOCKS_PER_SEC * 1000.0; -} - -static double time_limit = 0; -static int attempts = 0; // THIS is to avoid a "yield across C-call boundary" error -#define MAX_ATTEMPTS 10 - -static lua_Subidx module_idx = LUA_NOREF; -enum ModuleIdxs { - IN_HOOK_IDX = 1, - MAX_IDX, -}; - -#define KILL_CMD "SANDBOX KILL" - -// checks error message (stack peek) starts with given msg -// lua errors is in this format ":: " -static bool lua_error_startswith(lua_State *L, lua_Idx idx, cstr msg, size_t count) { - assert_ptr(msg, 3); - L_assert_string(idx); - - // lua manages the string, so we don't need to free() it ourselves :D - size_t errfull_count; - cstr errfull = lua_tolstring(L, idx, &errfull_count); - - if (errfull_count < count) { return false; } - if (errfull_count == count ) { - return string_eq(errfull, errfull_count, msg, count); - } - - // 0: initial search - // 1: past : - // 2: past : - int state = 0; - cstr errmsg = errfull; - for (size_t i = 0; state < 3 && i < errfull_count - count; ++i) { - switch (state) { - case 0: - case 1: - if (errfull[i] == ':') { - ++state; - if (i < errfull_count - count) { - errmsg = errfull + i + 1; // if there's no whitespace, just search here - } - } - break; - - case 2: - if (errfull[i] != ' ') { - ++state; - errmsg = errfull + i; - } - break; - } - } - - return string_startswith(errmsg, - errfull_count - (size_t)(errmsg - errfull), msg, count); -} - -#ifdef DEBUG -// lua: $globals.core.log(level, msg) -static void luanti_log(lua_State *L, cstr restrict level, cstr restrict msg) { - assert_str(level, 2); - assert_ptr(msg, 4); - - if (L_deepget_field(LUA_GLOBALS, "core", "log")) { - luaL_error(L, "Unable to deep get $global.core.log"); - } - lua_pushstring(L, level); - lua_pushstring(L, msg); - lua_call(L, 2, 0); -} -#else -#define luanti_log(...) -#endif - -static void hookf(lua_State *L, lua_Debug *ar) { - const lua_Idx reset = lua_gettop(L); - - if (L_deepget_subi(LUA_CREGISTRY, module_idx, IN_HOOK_IDX)) { - luaL_error(L, "Unable to deep get $module[IN_HOOK_IDX]"); - } - if (lua_isfunction(L, -1)) { - // lua: errcode, (stack push) = pcall(in_hook) - const int errcode = lua_pcall(L, 0, 0, 0); - if (errcode != 0) { - // lua: errmsg_len, errmsg = #(stack peek), (stack peek) - if (lua_error_startswith(L, -1, KILL_CMD, CSTR_COUNT(KILL_CMD))) { - lua_error(L); - // lua: else debug.sethook(); yield(); end - } else { - if (lua_isyieldable(L) || attempts >= MAX_ATTEMPTS) { - lua_settop(L, reset); - lua_sethook(L, nil, 0, 0); - lua_yield(L, 0); - return; - } else { - ++attempts; - } - return; - } - } - return; - } - - if (get_time_ms() - prev_time > time_limit) { - if (lua_isyieldable(L) || attempts >= MAX_ATTEMPTS) { - prev_time = get_time_ms(); - lua_settop(L, reset); - lua_sethook(L, nil, 0, 0); - lua_yield(L, 0); - } else { - ++attempts; - } - } -} - -/// autohook(int hook_time, double time_limit, lua_function|nil|none in_hook) -/// hook_time: same as in libox.coroutine.create_sandbox. Instructions per hook trigger -/// time_limit: same as in libox.coroutine.create_sandbox, but in ms. -/// Allowed execution time limit for the coroutine sandbox in milliseconds(!) -static int autohook(lua_State *L) { - const lua_Idx n = lua_gettop(L); - if (n < 2 || n > 3) { - luaL_error(L, "attach autohook() takes 2 or 3 arguments"); - } - - int hook_time; - L_assert_number(1); - L_assert_number(2); - if ((hook_time = (int)lua_tointeger(L, 1)) < 0) { - L_arg_expect_ll(1, "positive number", "negative number"); - } - if ((time_limit = lua_tonumber(L, 2)) < 0) { - L_arg_expect_ll(2, "positive number", "negative number"); - } - - if (lua_isfunction(L, 3)) { - if (L_deepset_subi(LUA_CREGISTRY, module_idx, IN_HOOK_IDX)) { - luaL_error(L, "Unable to deep set $module[IN_HOOK_IDX]"); - } - } else if (!lua_isnoneornil(L, 3)) { - L_arg_expect_la(3, "function or nil"); - } - lua_settop(L, 0); - - prev_time = get_time_ms(); - attempts = 0; - lua_sethook(L, hookf, LUA_MASKCOUNT, hook_time); - return 0; -} - -static int version(lua_State *L) { - lua_pushstring(L, AUTOHOOK_HASH); - lua_pushstring(L, LUAJIT_VERSION); - return 2; -} - -// the autohook module's table -static const luaL_Reg funcs [] = { - {"autohook", autohook}, - {"version", version}, - {nil, nil} -}; - -int luaopen_autohook(lua_State *L) {; - lua_createtable(L, MAX_IDX, 0); - module_idx = L_stack2ref(); - luaL_register(L, "autohook", funcs); - return 1; -} diff --git a/mods/libox/autohook/autohook.h b/mods/libox/autohook/autohook.h deleted file mode 100644 index 30daba3a..00000000 --- a/mods/libox/autohook/autohook.h +++ /dev/null @@ -1,3 +0,0 @@ -// This number is tied to the latest commit's UNIX time -#define AUTOHOOK_HASH "69d0480c1b562e26fc1668c3348c3c6071c4d41ffe410c4373cc70859b23fe19" -#define DEBUG 0 \ No newline at end of file diff --git a/mods/libox/autohook/autohook.h.in b/mods/libox/autohook/autohook.h.in deleted file mode 100644 index 9ab0e98c..00000000 --- a/mods/libox/autohook/autohook.h.in +++ /dev/null @@ -1,6 +0,0 @@ -// This number is tied to the latest commit's UNIX time -#define AUTOHOOK_HASH "${AUTOHOOK_HASH}" -${define DEBUG} -#if DEBUG == 0 -#undef DEBUG -#endif \ No newline at end of file diff --git a/mods/libox/autohook/build/.build_cache/5a/5a0144f26e0ed456ec3c30593cb46ff4 b/mods/libox/autohook/build/.build_cache/5a/5a0144f26e0ed456ec3c30593cb46ff4 deleted file mode 100644 index e974cfc5..00000000 Binary files a/mods/libox/autohook/build/.build_cache/5a/5a0144f26e0ed456ec3c30593cb46ff4 and /dev/null differ diff --git a/mods/libox/autohook/build/.build_cache/9e/9e19e71a9cd69ef70589b2dd347fcfee b/mods/libox/autohook/build/.build_cache/9e/9e19e71a9cd69ef70589b2dd347fcfee deleted file mode 100644 index faa6274e..00000000 Binary files a/mods/libox/autohook/build/.build_cache/9e/9e19e71a9cd69ef70589b2dd347fcfee and /dev/null differ diff --git a/mods/libox/autohook/build/.build_cache/9e/9e19e71a9cd69ef70589b2dd347fcfee.txt b/mods/libox/autohook/build/.build_cache/9e/9e19e71a9cd69ef70589b2dd347fcfee.txt deleted file mode 100644 index f08861a6..00000000 --- a/mods/libox/autohook/build/.build_cache/9e/9e19e71a9cd69ef70589b2dd347fcfee.txt +++ /dev/null @@ -1,14 +0,0 @@ -{ - errdata = "\27[01m\27[Kcc1:\27[m\27[K \27[01;35m\27[Kwarning: \27[m\27[Kcommand-line option ā€˜\27[01m\27[K-Weffc++\27[m\27[K’ is valid for C++/ObjC++ but not for C\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-unsafe-buffer-usage\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-gnu-zero-variadic-macro-arguments\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-newline-eof\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-extra-semi-stmt\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;35m\27[Kwarning: \27[m\27[Kcommand-line option ā€˜\27[01m\27[K-Weffc++\27[m\27[K’ is valid for C++/ObjC++ but not for C\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-gnu-line-marker\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-unsafe-buffer-usage\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-gnu-zero-variadic-macro-arguments\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-newline-eof\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-extra-semi-stmt\27[m\27[K’ may have been intended to silence earlier diagnostics\ -" -} \ No newline at end of file diff --git a/mods/libox/autohook/build/.build_cache/ad/addd798cbb6e56df5c2e6a9cb4a13da6 b/mods/libox/autohook/build/.build_cache/ad/addd798cbb6e56df5c2e6a9cb4a13da6 deleted file mode 100644 index 6acc417f..00000000 Binary files a/mods/libox/autohook/build/.build_cache/ad/addd798cbb6e56df5c2e6a9cb4a13da6 and /dev/null differ diff --git a/mods/libox/autohook/build/.build_cache/ad/addd798cbb6e56df5c2e6a9cb4a13da6.txt b/mods/libox/autohook/build/.build_cache/ad/addd798cbb6e56df5c2e6a9cb4a13da6.txt deleted file mode 100644 index f08861a6..00000000 --- a/mods/libox/autohook/build/.build_cache/ad/addd798cbb6e56df5c2e6a9cb4a13da6.txt +++ /dev/null @@ -1,14 +0,0 @@ -{ - errdata = "\27[01m\27[Kcc1:\27[m\27[K \27[01;35m\27[Kwarning: \27[m\27[Kcommand-line option ā€˜\27[01m\27[K-Weffc++\27[m\27[K’ is valid for C++/ObjC++ but not for C\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-unsafe-buffer-usage\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-gnu-zero-variadic-macro-arguments\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-newline-eof\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-extra-semi-stmt\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;35m\27[Kwarning: \27[m\27[Kcommand-line option ā€˜\27[01m\27[K-Weffc++\27[m\27[K’ is valid for C++/ObjC++ but not for C\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-gnu-line-marker\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-unsafe-buffer-usage\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-gnu-zero-variadic-macro-arguments\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-newline-eof\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-extra-semi-stmt\27[m\27[K’ may have been intended to silence earlier diagnostics\ -" -} \ No newline at end of file diff --git a/mods/libox/autohook/build/.build_cache/cd/cdd7dc4994587146ed446897d780874d b/mods/libox/autohook/build/.build_cache/cd/cdd7dc4994587146ed446897d780874d deleted file mode 100644 index feb21449..00000000 Binary files a/mods/libox/autohook/build/.build_cache/cd/cdd7dc4994587146ed446897d780874d and /dev/null differ diff --git a/mods/libox/autohook/build/.build_cache/cd/cdd7dc4994587146ed446897d780874d.txt b/mods/libox/autohook/build/.build_cache/cd/cdd7dc4994587146ed446897d780874d.txt deleted file mode 100644 index f08861a6..00000000 --- a/mods/libox/autohook/build/.build_cache/cd/cdd7dc4994587146ed446897d780874d.txt +++ /dev/null @@ -1,14 +0,0 @@ -{ - errdata = "\27[01m\27[Kcc1:\27[m\27[K \27[01;35m\27[Kwarning: \27[m\27[Kcommand-line option ā€˜\27[01m\27[K-Weffc++\27[m\27[K’ is valid for C++/ObjC++ but not for C\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-unsafe-buffer-usage\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-gnu-zero-variadic-macro-arguments\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-newline-eof\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-extra-semi-stmt\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;35m\27[Kwarning: \27[m\27[Kcommand-line option ā€˜\27[01m\27[K-Weffc++\27[m\27[K’ is valid for C++/ObjC++ but not for C\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-gnu-line-marker\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-unsafe-buffer-usage\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-gnu-zero-variadic-macro-arguments\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-newline-eof\27[m\27[K’ may have been intended to silence earlier diagnostics\ -\27[01m\27[Kcc1:\27[m\27[K \27[01;36m\27[Knote: \27[m\27[Kunrecognized command-line option ā€˜\27[01m\27[K-Wno-extra-semi-stmt\27[m\27[K’ may have been intended to silence earlier diagnostics\ -" -} \ No newline at end of file diff --git a/mods/libox/autohook/build/.deps/autohook/linux/x86_64/release/autohook.c.o.d b/mods/libox/autohook/build/.deps/autohook/linux/x86_64/release/autohook.c.o.d deleted file mode 100644 index 95a6e69d..00000000 --- a/mods/libox/autohook/build/.deps/autohook/linux/x86_64/release/autohook.c.o.d +++ /dev/null @@ -1,35 +0,0 @@ -{ - depfiles = "autohook.o: autohook.c autohook.h utils.c\ -", - depfiles_format = "gcc", - files = { - "autohook.c" - }, - values = { - "/usr/bin/gcc", - { - "-m64", - "-fPIC", - "-std=c11", - "-Ibuild/.gens/autohook/linux/x86_64/release/platform/windows/idl", - "-isystem", - "/usr/include/luajit-2.1", - "-Wall", - "-Wextra", - "-Wpedantic", - "-Wall", - "-Wextra", - "-Weffc++", - "-Wno-extra-semi-stmt", - "-Wno-newline-eof", - "-Wno-declaration-after-statement", - "-Wno-unused-parameter", - "-Wno-unused-function", - "-Wno-missing-prototypes", - "-Wno-unused-macros", - "-Wno-switch-default", - "-Wno-gnu-zero-variadic-macro-arguments", - "-Wno-unsafe-buffer-usage" - } - } -} \ No newline at end of file diff --git a/mods/libox/autohook/build/.deps/autohook/linux/x86_64/release/autohook.h.in.d b/mods/libox/autohook/build/.deps/autohook/linux/x86_64/release/autohook.h.in.d deleted file mode 100644 index 0e18c858..00000000 --- a/mods/libox/autohook/build/.deps/autohook/linux/x86_64/release/autohook.h.in.d +++ /dev/null @@ -1,5 +0,0 @@ -{ - files = { - "autohook.h.in" - } -} \ No newline at end of file diff --git a/mods/libox/autohook/build/.deps/autohook/linux/x86_64/release/libautohook.so.d b/mods/libox/autohook/build/.deps/autohook/linux/x86_64/release/libautohook.so.d deleted file mode 100644 index 5d893c60..00000000 --- a/mods/libox/autohook/build/.deps/autohook/linux/x86_64/release/libautohook.so.d +++ /dev/null @@ -1,15 +0,0 @@ -{ - files = { - "build/.objs/autohook/linux/x86_64/release/autohook.c.o", - "build/.objs/autohook/linux/x86_64/release/utils.c.o" - }, - values = { - "/usr/bin/g++", - { - "-shared", - "-m64", - "-fPIC", - "-lluajit-5.1" - } - } -} \ No newline at end of file diff --git a/mods/libox/autohook/build/.deps/autohook/linux/x86_64/release/utils.c.o.d b/mods/libox/autohook/build/.deps/autohook/linux/x86_64/release/utils.c.o.d deleted file mode 100644 index 1654d89c..00000000 --- a/mods/libox/autohook/build/.deps/autohook/linux/x86_64/release/utils.c.o.d +++ /dev/null @@ -1,35 +0,0 @@ -{ - depfiles = "utils.o: utils.c\ -", - depfiles_format = "gcc", - files = { - "utils.c" - }, - values = { - "/usr/bin/gcc", - { - "-m64", - "-fPIC", - "-std=c11", - "-Ibuild/.gens/autohook/linux/x86_64/release/platform/windows/idl", - "-isystem", - "/usr/include/luajit-2.1", - "-Wall", - "-Wextra", - "-Wpedantic", - "-Wall", - "-Wextra", - "-Weffc++", - "-Wno-extra-semi-stmt", - "-Wno-newline-eof", - "-Wno-declaration-after-statement", - "-Wno-unused-parameter", - "-Wno-unused-function", - "-Wno-missing-prototypes", - "-Wno-unused-macros", - "-Wno-switch-default", - "-Wno-gnu-zero-variadic-macro-arguments", - "-Wno-unsafe-buffer-usage" - } - } -} \ No newline at end of file diff --git a/mods/libox/autohook/build/.objs/autohook/linux/x86_64/release/autohook.c.o b/mods/libox/autohook/build/.objs/autohook/linux/x86_64/release/autohook.c.o deleted file mode 100644 index 6acc417f..00000000 Binary files a/mods/libox/autohook/build/.objs/autohook/linux/x86_64/release/autohook.c.o and /dev/null differ diff --git a/mods/libox/autohook/build/.objs/autohook/linux/x86_64/release/utils.c.o b/mods/libox/autohook/build/.objs/autohook/linux/x86_64/release/utils.c.o deleted file mode 100644 index faa6274e..00000000 Binary files a/mods/libox/autohook/build/.objs/autohook/linux/x86_64/release/utils.c.o and /dev/null differ diff --git a/mods/libox/autohook/hash.ps1 b/mods/libox/autohook/hash.ps1 deleted file mode 100644 index 274b6438..00000000 --- a/mods/libox/autohook/hash.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -# Only powershell needs ts cuz it sucks -$stream = New-Object System.IO.MemoryStream -foreach ($file in $args) { - [byte[]]$data = [System.IO.File]::ReadAllBytes($file) - $stream.Write($data, 0, $data.Length) -} - -$stream.Position = 0 -$hasher = [System.Security.Cryptography.SHA256]::Create() -$hash = $hasher.ComputeHash($stream) -$stream.Dispose() - -# Convert to readable hex string -$hex = -join ($hash | ForEach-Object { $_.ToString("x2") }) -Write-Output $hex diff --git a/mods/libox/autohook/hash.sh b/mods/libox/autohook/hash.sh deleted file mode 100644 index c86c303f..00000000 --- a/mods/libox/autohook/hash.sh +++ /dev/null @@ -1,5 +0,0 @@ -if command -v sha256sum >/dev/null 2>/dev/null; then - cat "$@" | sha256sum | awk '{ print $1 }' -elif command -v shasum >/dev/null 2>/dev/null; then - cat "$@" | shasum | awk '{ print $1 }' -fi \ No newline at end of file diff --git a/mods/libox/autohook/helpers.lua b/mods/libox/autohook/helpers.lua deleted file mode 100644 index aa3859e9..00000000 --- a/mods/libox/autohook/helpers.lua +++ /dev/null @@ -1,42 +0,0 @@ -if core ~= nil then error('DO NOT RUN THIS FROM LUANTI') end - -function get_sha256(filepaths) - if type(filepaths) == 'string' then - if not os.isfile(filepaths) then - raise('[get_sha256] File not found: ' ..filepaths) - end - filepaths = {filepaths} - elseif type(filepaths) == 'table' then - for i=1, #filepaths do - local filepath = filepaths[i] - if not os.isfile(filepath) then - raise('[get_sha256] File not found: ' ..filepath) - end - end - else - raise('[get_sha256] bad argument #1 (string or table expected, got ' ..type(filepaths) ..')') - end - - -- find_program caches its results, so it's fine to repeatedly call this - import('lib.detect.find_program') - local cmd - local args = {} - if os.is_host('windows') or os.shell() == 'pwsh' then - -- also for pwsh linux user... crazy!! - cmd = find_program('pwsh') or find_program('powershell') - table.join2(args, {'hash.ps1'}, filepaths) - else - cmd = find_program('sh') - table.join2(args, {'hash.sh'}, filepaths) - end - - -- this is so so so cursed, and without select() it's not possible to bring pcall back... - local result - try { function() - result = os.iorunv(cmd, args) - end, catch { function(errors) - print("Failed to compute SHA256: " .. errors) - end } - } - return result:trim() -end \ No newline at end of file diff --git a/mods/libox/autohook/libautohook.so b/mods/libox/autohook/libautohook.so deleted file mode 100755 index dcadd1be..00000000 Binary files a/mods/libox/autohook/libautohook.so and /dev/null differ diff --git a/mods/libox/autohook/module-version.txt b/mods/libox/autohook/module-version.txt deleted file mode 100644 index f8b4079a..00000000 --- a/mods/libox/autohook/module-version.txt +++ /dev/null @@ -1 +0,0 @@ -35d53973f1de456e9432d5d69003d54d116507f5b90832ea04f677b7cd3327cd \ No newline at end of file diff --git a/mods/libox/autohook/utils.c b/mods/libox/autohook/utils.c deleted file mode 100644 index 5c9adee7..00000000 --- a/mods/libox/autohook/utils.c +++ /dev/null @@ -1,179 +0,0 @@ -#include -#include -#include -#include -#include - -// a bit of macros never hurt nobody -// also, it's safe to assume that most compilers handle , ##__VA_ARGS__ like GCC -#define MAX_VA_ARGS(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,...) a18 -#define VA_COUNT(...) MAX_VA_ARGS(dummy, ##__VA_ARGS__, 16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0) - -typedef const char* cstr; -typedef char* cstr_mut; -#define CSTR_COUNT(x) sizeof(x)-1 - -// like home -#define nil NULL - -static bool string_eq( - cstr restrict s1, size_t count1, - cstr restrict s2, size_t count2) { - - if (count1 != count2) { return false; } - for (size_t i = 0; i < count1; ++i) { - if (s1[i] != s2[i]) { return false; } - } - return true; -} - -static bool string_startswith( - cstr restrict long_str, size_t long_count, - cstr restrict short_str, size_t short_count) { - - if (long_count < short_count) { return false; } - for (size_t i = 0; i < short_count; ++i) { - if (long_str[i] != short_str[i]) { return false; } - } - return true; -} - -#define LUA_CREGISTRY LUA_REGISTRYINDEX -#define LUA_GLOBALS LUA_GLOBALSINDEX - -// indexes the stack, or a pseudo-index -typedef int lua_Idx; - -// indexes a lua list/array/table, including the tables from pseudo-indices -typedef int lua_Subidx; - -#define L_arg_expect_xx(idx, expect, got) luaL_argerror(L, (idx), \ - lua_pushfstring(L, "%s expected, got %s", (expect), (got))) -#define L_arg_expect_lx(idx, expect, got) luaL_argerror(L, (idx), \ - lua_pushfstring(L, expect " expected, got %s", (got))) -#define L_arg_expect_ll(idx, expect, got) luaL_argerror(L, (idx), expect "expected, got " got) -#define L_arg_expect_xa(idx, expect) luaL_argerror(L, (idx), \ - lua_pushfstring(L, "%s expected, got %s", (expect), luaL_typename(L, (idx)))) -#define L_arg_expect_la(idx, expect) luaL_argerror(L, (idx), \ - lua_pushfstring(L, expect " expected, got %s", luaL_typename(L, (idx)))) - -#define L_carg_expect_xxx(narg, expect, got) \ - luaL_error(L, lua_pushfstring(L, "bad argument #%f to C function '%s' (%s expected, got %s)", (narg), __func__, (expect), (got))) -#define L_carg_expect_xll(narg, expect, got) \ - luaL_error(L, lua_pushfstring(L, "bad argument #%f to C function '%s' (" expect " expected, got " got ")", (narg), __func__)) - -// checks if the pointer is nil/NULL. raise error to Lua runtime -#define assert_ptr(p, narg) \ - if ((p) == nil) { L_carg_expect_xll(narg, "valid pointer", "nil/NULL"); } -// check nil AND non-empty -#define assert_str(p, narg) assert_ptr(p, narg); \ - if ((p)[0] == '\0') { L_carg_expect_xll(narg, "non-empty string", "empty string"); } - -#define L_stack2ref() luaL_ref(L, LUA_CREGISTRY) -static bool lua_ref2stack(lua_State* L, const lua_Subidx ref) { - if (ref == LUA_NOREF || ref == LUA_REFNIL) { return false; } - lua_rawgeti(L, LUA_CREGISTRY, ref); - luaL_unref(L, LUA_CREGISTRY, ref); - return true; -} -#define L_ref2stack(ref) lua_ref2stack(L, ref) - -static bool lua_is_container(lua_State *L, const lua_Idx idx) { - return idx == LUA_CREGISTRY || idx == LUA_GLOBALS || lua_istable(L, idx); -} -#define L_assert_container(idx) \ - if (!lua_is_container(L, (idx))) { L_arg_expect_la(idx, "table or pseudo-index"); } - -#define L_assert_number(idx) \ - if (!lua_isnumber(L, (idx))) { L_arg_expect_la(idx, "number"); } - -#define L_assert_string(idx) \ - if (!lua_isstring(L, (idx))) { L_arg_expect_la(idx, "string"); } - -/* ------------------------ deep getters and setters ------------------------ */ - -static bool lua_deepget_subi(lua_State *L, lua_Idx idx, size_t count, const lua_Subidx* subis) { - L_assert_container(idx); - assert_ptr(subis, 4); - if (count < 1) { - L_carg_expect_xll(3, "positive number of subidx", "bad number of subidx"); - } - if (count == 1) { - lua_rawgeti(L, idx, subis[0]); - // waste of compute... bro just use rawgeti - return false; - } - - const lua_Idx reset = lua_gettop(L); - lua_rawgeti(L, idx, subis[0]); - for (size_t i = 1; i < count; ++i) { - if (!lua_istable(L, -1)) { - lua_settop(L, reset); - return true; - } - lua_rawgeti(L, -1, subis[i]); - } - const lua_Idx got = L_stack2ref(); - lua_settop(L, reset); - L_ref2stack(got); - return false; -} -// lua: (push stack) = (table at idx)[subi1][subi2][subi3][...] -#define L_deepget_subi(idx, ...) \ - lua_deepget_subi(L, (idx), VA_COUNT(__VA_ARGS__), (lua_Subidx[]){ __VA_ARGS__ }) - -static bool lua_deepset_subi(lua_State *L, lua_Idx idx, bool fill_in, size_t count, const lua_Subidx* subis) { - L_assert_container(idx); - assert_ptr(subis, 4); - if (count < 1) { - L_carg_expect_xll(3, "positive number of subidx", "bad number of subidx"); - } - if (count == 1) { - lua_rawseti(L, idx, subis[0]); - // waste of compute... bro just use rawseti - return true; - } - - bool filling_in = false; - const lua_Idx value = L_stack2ref(); - const lua_Idx reset = lua_gettop(L); - lua_rawgeti(L, idx, subis[0]); - if (!lua_istable(L, -1)) { - if (!(filling_in || (fill_in && lua_isnil(L, -1)))) { - lua_settop(L, reset); - return true; - } - filling_in = true; - lua_pop(L, 1); - lua_createtable(L, 0, 1); - lua_pushvalue(L, -1); - lua_rawseti(L, idx, subis[0]); - } - - const size_t last = count - 1; - for (size_t i = 1; i < last; ++i) { - lua_rawgeti(L, -1, subis[i]); - if (!lua_istable(L, -1)) { - if (!(filling_in || (fill_in && lua_isnil(L, -1)))) { - lua_settop(L, reset); - return true; - } - filling_in = true; - lua_pop(L, 1); - lua_createtable(L, 0, 1); - lua_pushvalue(L, -1); - lua_rawseti(L, -2, subis[0]); - } - } - L_ref2stack(value); - lua_rawseti(L, -2, subis[last]); - lua_settop(L, reset); - return false; -} -// lua: (table at idx)[subi1][subi2][subi3][...] = (stack pop) -#define L_deepset_subi(idx, ...) \ - lua_deepset_subi(L, (idx), false, VA_COUNT(__VA_ARGS__), (lua_Subidx[]){ __VA_ARGS__ }) -// lua: (table at idx)[subi1][subi2][subi3][...] = (stack pop) -// fills the subtable in if it's created yet. good for initializing table -#define L_deepset_fill_subi(idx, ...) \ - lua_deepset_subi(L, (idx), true, VA_COUNT(__VA_ARGS__), (lua_Subidx[]){ __VA_ARGS__ }) diff --git a/mods/libox/autohook/xmake.lua b/mods/libox/autohook/xmake.lua deleted file mode 100644 index de19a7b4..00000000 --- a/mods/libox/autohook/xmake.lua +++ /dev/null @@ -1,119 +0,0 @@ -if core ~= nil then error('DO NOT RUN THIS FROM LUANTI') end - - -option('Iluajit') - set_showmenu(true) - set_description('Custom LuaJIT includes/ directory') -option_end() - -option('Lluajit') - set_showmenu(true) - set_description('Custom LuaJIT libs/ directory') -option_end() - -add_requires('luajit', { system = true, configs = { shared = false }, optional = true}) - -local clang_flags = { - "-Wall", "-Wextra", "-Wpedantic", "-Weverything", - -- useless/meh warnings -- - "-Wno-extra-semi-stmt", "-Wno-newline-eof", - "-Wno-declaration-after-statement", "-Wno-unused-parameter", - "-Wno-unused-function", "-Wno-missing-prototypes", "-Wno-unused-macros", - "-Wno-switch-default", - -- other warnings -- - -- only a problem if you're using some wacky compiler - "-Wno-gnu-zero-variadic-macro-arguments", - - -- C + Lua doesn't provide easy ways to protect from this. ignoring... - "-Wno-unsafe-buffer-usage" -} -local gcc_flags = { - "-Wall", "-Wextra", "-Wpedantic", - -- uhh idk -} - -target('autohook') - set_kind('shared') - set_basename('autohook') - set_targetdir('.') - add_files('autohook.c', 'utils.c') - set_configdir('.') - add_configfiles('autohook.h.in') - - set_languages("c11") - - add_packages('luajit') - - on_load(function(target) - import('core.tool.compiler') - local cc = compiler.compargv('dummy.c', { target = target }) - if cc and path.basename(cc):match('clang') then - for i=1,#clang_flags do - target:add('cflags', clang_flags[i]) - end - else - for i=1,#gcc_flags do - target:add('cflags', gcc_flags[i]) - end - end - - local luajit_include = get_config('Iluajit') - if luajit_include then - assert(os.isfile(path.join(luajit_include, 'luajit.h')), - ('Failed finding LuaJIT headers in '):format(luajit_include)) - target:add('includedirs', luajit_include) - end - - local luajit_lib = get_config('Lluajit') - if luajit_lib then - import('lib.detect.find_library') - local found_lib_51 = find_library('luajit-5.1', { luajit_lib }, { kind = 'static' }) - local found_lib_simple = find_library('luajit', { luajit_lib }, { kind = 'static' }) - - assert(found_lib_51 or found_lib_simple, - ('Failed finding LuaJIT library in : '):format(luajit_lib)) - target:add('linkdirs', luajit_lib) - if found_lib_51 then target:add('links', 'luajit-5.1') end - if found_lib_simple then target:add('links', 'luajit') end - end - - import('helpers') - local hash = helpers.get_sha256{'autohook.c', 'utils.c'} - target:set('configvar', 'AUTOHOOK_HASH', hash) - io.writefile("module-version.txt", hash) - end) - - - before_build(function(target) - printf('checking for LuaJIT version ... ') - - local get_luajit_version = [[ -#include -#include -int main(int argc, char** argv) { - puts(LUAJIT_VERSION); - return 0; -}]] - local ok, luajit_version = target:check_csnippets({ test = get_luajit_version }, { - tryrun = true, - output = true, - -- includes = {"path/to/headers.h"}, - configs = {languages = 'c11'}}) - - local errmsg = ('Outdated LuaJIT version! Please update it or build a LuaJIT rolling release (current: %s)'):format(luajit_version) - - -- must at least be 2.1.* - assert(luajit_version:startswith('LuaJIT 2.1.'), errmsg) - - -- this is exactly how LuaJIT developer wants downstream users to check - -- the rolling release versions. the beta versions are 2.1.0. but it's - -- more robust if we can just put which commit to cut off support. Also - -- people might build LuaJIT version that default to 2.1.ROLLING cuz - -- they didn't allow the build script to determine the last commit - -- timestamp. - local supported = 1692580715 -- commit c3459468 first 2.1.ROLLING release - assert(luajit_version == 'LuaJIT 2.1.ROLLING' - or tonumber(luajit_version:sub(12):match('^%d+')) >= supported, errmsg) - - cprint('${bright green}' ..luajit_version) - end) \ No newline at end of file diff --git a/mods/libox/coroutine.lua b/mods/libox/coroutine.lua deleted file mode 100644 index 3cdaa4d7..00000000 --- a/mods/libox/coroutine.lua +++ /dev/null @@ -1,364 +0,0 @@ -local active_sandboxes = {} -local api = {} - - -api.settings = { - memory_treshold = 5, - gc = { - time_treshold = 10 * 60, -- if a sandbox has been untouched then collect it - number_of_sandboxes = 60, - auto = false, - interval = 60 - }, -} - -local settings = minetest.settings - -local function setting(v, thing) - local t = type(v) - local number = function(n) return n end -- noop - if t == "number" then - number = tonumber - end - - if t ~= "boolean" then - return number(settings:get(thing)) - else - return settings:get_bool(thing) - end -end - -local function do_the_settings_thing(name, table) - for k, v in pairs(table) do - if type(v) == "table" then - do_the_settings_thing(name .. "." .. k, v) - else - table[k] = setting(v, name .. "." .. k) or v - end - end -end - -do_the_settings_thing("libox", api.settings) - -local attach_autohook = libox_autohook_module and libox_autohook_module.autohook -local function attach_hook(sandbox) - if sandbox.autohook and attach_autohook then - local hook - if sandbox.in_hook then - hook = sandbox.in_hook() - end - attach_autohook( - sandbox.hook_time or libox.default_hook_time, - (sandbox.time_limit or libox.default_time_limit) / 1000, - hook) - else - debug.sethook(sandbox.in_hook(), "", sandbox.hook_time or libox.default_hook_time) - end -end - -local BYTE_A, BYTE_Z = string.byte("A"), string.byte("Z") -local function rand_text(n) - local out = "" - for _ = 1, n do - out = out .. string.char(math.random(BYTE_A, BYTE_Z)) -- [A-Z] - end - return out -end - - - -function api.create_sandbox(def) - local ID = def.ID or rand_text(10) - local in_hook = def.in_hook - if not (def.autohook and attach_autohook) then - in_hook = in_hook or libox.coroutine.get_default_hook(def.time_limit or libox.default_time_limit) - end - active_sandboxes[ID] = { - code = def.code, - is_garbage_collected = def.is_garbage_collected or true, - env = def.env or {}, - in_hook = in_hook, - function_wrap = def.function_wrap or function(f) return f end, - last_ran = os.clock(), -- for gc and logging - hook_time = def.hook_time or libox.default_hook_time, - size_limit = def.size_limit or 1024 * 1024 * 5, -- 5 megabytes - autohook = def.autohook or false, - } - return ID -end - -function api.create_thread(sandbox) - -- prohibited by mod security anyway, basically bytecode is fancy stuff that allows rce and we dont want that - if sandbox.code:byte(1) == 27 then - return false, "Bytecode was not allowed." - -- *mod security would prevent it anyway* but just in case someone turned that off - end - - local f, msg = loadstring(sandbox.code, "(libox sandbox: coroutine)") - if not f then - return false, msg - end - - - setfenv(f, sandbox.env) - - if rawget(_G, "jit") then - jit.off(f, true) - -- turn jit off for that function and yes this is needed or the user can repeat until false, sorry - end - - f = sandbox.function_wrap(f) - sandbox.thread = coroutine.create(f) - return true -end - -function api.is_sandbox_dead(id) - local sandbox = active_sandboxes[id] - if sandbox == nil then return true end - if sandbox.thread == nil then return false end -- api.run_sandbox will work just fine - if coroutine.status(sandbox.thread) == "dead" then return true end - return false -end - -function api.locals(val, f_thread) - local ret = { - _F = "", -- the function itself, weighed and put using string.dump, if thread this is ignored - _L = {}, -- Locals - _U = {} -- Upvalues - } - - local getinfo, getlocal, getupvalue = debug.getinfo, debug.getlocal, debug.getupvalue - - local index - if type(val) == "thread" then - local level = getinfo(val, 1, "u") - if level ~= nil then - index = 1 - while true do - local k, v = getlocal(val, 1, index) - if k ~= nil then - ret._L[k] = v - else - break - end - index = index + 1 - end - - if level.nups > 0 then - index = 1 - local f = getinfo(val, 1, "f").func - while true do - local k, v = getupvalue(f, index) - if k ~= nil then - ret._U[k] = v - else - break - end - index = index + 1 - end - end - end - elseif type(val) == "function" then - local func_info = getinfo(f_thread, val, "Su") - if not func_info or func_info.what == "C" then - -- C functions are not weighed because... well... they can't be - return {} - end - local f_size = string.dump(val) - ret._F = f_size - index = 1 - while true do - local k, v = getlocal(val, index) - if k ~= nil then - ret._L[k] = v - else - break - end - index = index + 1 - end - if func_info.nups > 0 then - index = 1 - while true do - local k, v = getupvalue(val, index) - if k ~= nil then - ret._U[k] = v - else - break - end - index = index + 1 - end - end - end - return ret -end - -function api.get_size(env, seen, thread, recursed) - local deferred_weigh_locals = {} - if not recursed then - deferred_weigh_locals[#deferred_weigh_locals + 1] = thread - end - - local function internal(x, seen) -- luacheck: ignore - local t = type(x) - if t == "string" then - return #x + 25 - elseif t == "number" then - return 8 - elseif t == "boolean" then - return 1 - elseif t == "table" and not seen[x] then - local cost = 8 - seen[x] = true - for k, v in pairs(x) do - local k_cost = internal(k, seen) - local v_cost = internal(v, seen) - cost = cost + k_cost + v_cost - end - return cost - elseif t == "function" and not seen[x] then - -- oh the fun! - seen[x] = true - deferred_weigh_locals[#deferred_weigh_locals + 1] = x - return 0 -- deffered - elseif t == "thread" and not seen[x] then - seen[x] = true - deferred_weigh_locals[#deferred_weigh_locals + 1] = x - return 0 -- deffered - else - return 0 - end - end - - local retv = internal(env, seen) - if debug.getlocal ~= nil and debug.getupvalue ~= nil then - for i = 1, #deferred_weigh_locals do - local v = deferred_weigh_locals[i] - if not seen[v] then - local their_locals = api.locals(v, thread) - - local size = api.get_size(their_locals, seen, thread, true) - retv = retv + size - end - end - end - - return retv -end - -function api.size_check(env, lim, thread) - if thread == nil then error("Thread is nil! you can't check the size!") end - local size = api.get_size(env, {}, thread, false) - return size < lim -end - -function api.get_default_hook(max_time) - return function() - local time = minetest.get_us_time - - local start_time = time() - return function() - if time() - start_time > max_time then - debug.sethook() - error("Code timed out! Reason: Time limit exceeded, the limit:" .. - tostring(max_time / 1000) .. "ms, the program took:" .. ((time() - start_time) / 1000), 2) - end - end - end -end - -function api.run_sandbox(ID, value_passed) - if libox.disabled then - return false, "Libox is disabled. Please wait until the server admins re-enable it." - end - - local sandbox = active_sandboxes[ID] - if sandbox == nil then - return false, "Sandbox not found. (Garbage collected?)" - end - - sandbox.last_ran = os.clock() - - if sandbox.thread == nil then - local ok, errmsg = api.create_thread(sandbox) - if ok == false then - return false, errmsg - end - end - - local thread = sandbox.thread - if coroutine.status(thread) == "dead" then - return false, "The coroutine is dead, nothing to do." - end - - - local ok, errmsg_or_value - local pcall_ok, pcall_errmsg - -- "nested pcall just in case" i knowww its bad and it sounds bad but yeah i had crashes when there wasnt a pcall adn yeaah - local no_strange_bug_happened = pcall(function() - pcall_ok, pcall_errmsg = pcall(function() - attach_hook(sandbox) - getmetatable("").__index = sandbox.env.string - ok, errmsg_or_value = coroutine.resume(thread, value_passed) - debug.sethook() - end) - debug.sethook() - getmetatable("").__index = string - end) - debug.sethook() - getmetatable("").__index = string - - if not no_strange_bug_happened then - return false, "Strange bug happened, but yes - the sandbox timed out." - end - - local size_check = api.size_check(sandbox.env, sandbox.size_limit, thread) - if not size_check then return false, "Out of memory!" end - - if not pcall_ok then - return false, pcall_errmsg - end - - if not ok then - return false, errmsg_or_value - else - return true, errmsg_or_value - end -end - -function api.garbage_collect() - local number_of_sandboxes = 0 - local to_be_collected = {} - local current_time = os.clock() - - for k, v in pairs(active_sandboxes) do - if v.is_garbage_collected then - number_of_sandboxes = number_of_sandboxes + 1 - - local difftime = current_time - v.last_ran - if difftime > api.settings.gc.time_treshold then - to_be_collected[#to_be_collected + 1] = k - end - end - end - - if number_of_sandboxes < api.settings.gc.number_of_sandboxes then return false end - for i = 1, #to_be_collected do - active_sandboxes[to_be_collected[i]] = nil - end - - return #to_be_collected -end - --- export -api.active_sandboxes = active_sandboxes -libox.coroutine = api - -local function start_timer() - minetest.after(api.settings.gc.interval, function() - api.garbage_collect() - start_timer() - end) -end -if api.settings.gc.auto then - start_timer() -end diff --git a/mods/libox/env.lua b/mods/libox/env.lua deleted file mode 100644 index c3829b45..00000000 --- a/mods/libox/env.lua +++ /dev/null @@ -1,292 +0,0 @@ -local BYTECODE_CHAR = 27 - -local function wrap(f, obj) - return function(...) - return f(obj, ...) - end -end - -function libox.safe.rep(str, n) - if #str * n > 64000 then - error("string length overflow", 2) - end - return string.rep(str, n) -end - -function libox.safe.PcgRandom(seed, seq) - if seq and #seq > 1000 then error("Sequence too large, size limit is 1000", 2) end - local pcg = PcgRandom(seed, seq) - - -- now make the interface - local interface = { - next = wrap(pcg.next, pcg), - rand_normal_dist = function(min, max, num_trials) - if num_trials and num_trials > 50 then - error("too many trials", 2) - end - return pcg:rand_normal_dist(min, max, num_trials) - end - } - return interface -end - -function libox.safe.PerlinNoise(noiseparams) - if type(noiseparams) ~= "table" then return false, "noiseparams is a table, deprecated syntax is not supported." end - - noiseparams.persistence = noiseparams.persistence or noiseparams.persist - noiseparams.persist = nil - local check, element = libox.type_check(noiseparams, { - offset = libox.type("number"), - scale = libox.type("number"), - spread = libox.type_vector, - seed = libox.type("number"), - octaves = libox.type("number"), - persistence = libox.type("number"), - lacunarity = libox.type("number"), - flags = function(x) - if x ~= nil then - return type(x) == "string" - else - return true - end - end, - }) - - if not check then - return false, element - end - - local core = PerlinNoise(noiseparams) - local interface = { - get_2d = wrap(core.get_2d, core), - get_3d = wrap(core.get_3d, core) - } - return interface -end - -function libox.safe.pcall(f, ...) - local ret_values = { pcall(f, ...) } - if not debug.gethook() then - error("Code timed out!", 2) - end - return unpack(ret_values) -end - -function libox.safe.xpcall(f, handler, ...) - local ret_values = { - xpcall(f, function(...) - if not debug.gethook() then - error("Code timed out!", 2) - return - end - return handler(...) - end, ...) - } - - if not debug.gethook() then - error("Code timed out!", 2) - end - - return unpack(ret_values) -end - -function libox.safe.get_loadstring(env) -- chunkname is ignored - return function(code, chunkname) - if chunkname ~= nil then - error("Adding a chunkname is forbidden", 2) - end - if type(code) == "string" and #code > 64000 then error("Code too long :/", 2) end - if string.byte(code, 1) == BYTECODE_CHAR then - error("dont sneak in bytecode (mod security will prevent you anyway)", 2) - end - local f, errmsg = loadstring(code) - if f == nil then - return nil, errmsg - end - setfenv(f, env) - - if rawget(_G, "jit") then - jit.off(f, true) - end - - return f, errmsg - end -end - ---[[ - safe_date is from the mooncontroller mod - licensed under LGPLv3, by OgelGames -]] --- Wraps os.date to only replace valid formats, --- ignoring invalid ones that would cause a hard crash. -local TIME_MAX = 32535244800 -- 01/01/3001 --- todo: change on 01/01/3001 -local function safe_date(str, time) - if type(time) ~= "number" then - time = os.time() - elseif time < 0 or time >= TIME_MAX then - return nil - end - - if type(str) ~= "string" then - return os.date("%c", time) - end - if str == "*t" then - return os.date("*t", time) - end - str = string.gsub(str, "%%[aAbBcdHImMpSwxXyY]", function(s) - return os.date(s, time) - end) - return str -end - -local function datetable() - return os.date("*t") -end - -function libox.create_basic_environment() - --[[ - get the safest, least strict lua environment - *for the "normal" sandbox, when using the "coroutine" sandbox you will need to add coroutine.yield - and edit pcall/xpcall to run coroutine.yield instead of erroring - - ]] - - -- INCLUDES: basic lib (minus coroutine, add that yourself if you need to), string, table, math, bit, os, a bit of minetest, some minetest classes - -- is meant to be added on top of - local env = { - assert = assert, - error = error, - collectgarbage = function(arg) - if arg ~= "count" then error("The only valid mode to collectgarbage is count") end - return collectgarbage("count") - end, - ipairs = ipairs, - pairs = pairs, - next = next, - pcall = libox.safe.pcall, - xpcall = libox.safe.xpcall, - select = select, - unpack = function(t, a, b) -- from mooncontroller - assert(not b or b < 2 ^ 30) - return unpack(t, a, b) - end, - - tonumber = tonumber, - tostring = tostring, - type = type, - - has_autohook = libox.has_autohook, - } - env.loadstring = libox.safe.get_loadstring(env) - env._G = env - - env.string = { - byte = string.byte, - char = string.char, - dump = string.dump, - find = function(s, pattern, init, plain) - if not plain then - error( - "string.find: the fourth parameter (plain) must be true, if you need patterns, use pat.find instead", - 2) - end - return string.find(s, pattern, init, true) - end, - format = string.format, - len = string.len, - lower = string.lower, - rep = libox.safe.rep, - reverse = string.reverse, - sub = string.sub, - upper = string.upper, - -- minetest helpers - trim = string.trim, - split = function(str, delim, include_empty, max_splits, sep_is_pattern) - if sep_is_pattern == true then - error("The fourth argument (sep_is_pattern) must be false", 2) - end - return string.split(str, delim, include_empty, max_splits, false) - end, - } - - env.table = { - insert = table.insert, - maxn = table.maxn, - remove = table.remove, - sort = table.sort, - concat = table.concat, - -- minetest only - indexof = table.indexof, - copy = table.copy, - insert_all = table.insert_all, - key_value_swap = table.key_value_swap, - shuffle = table.shuffle, - - -- luajit only - move = table.move, - } - - - env.math = {} - for _, v in ipairs({ - "abs", "acos", "asin", "atan", "atan2", "ceil", "cos", "cosh", "deg", "exp", "floor", - "fmod", "frexp", "huge", "ldexp", "log", "log10", "max", "min", "modf", "pi", "pow", - "rad", "random", "sin", "sinh", "sqrt", "tan", "tanh", - - -- minetest helpers - "hypot", "sign", "factorial", "round" - }) do - env.math[v] = math[v] - end - - env.bit = table.copy(bit) - - env.vector = table.copy(vector) - env.vector.metatable = nil -- it's useless to add it, and it's undocumented - env.os = { - clock = os.clock, - datetable = datetable, - difftime = os.difftime, - time = os.time, - date = safe_date, - } - - env.minetest = { - hash_node_position = minetest.hash_node_position, - get_position_from_hash = minetest.get_position_from_hash, - formspec_escape = libox.sandbox_lib_f(minetest.formspec_escape), - explode_table_event = libox.sandbox_lib_f(minetest.explode_table_event), - explode_textlist_event = libox.sandbox_lib_f(minetest.explode_textlist_event), - explode_scrollbar_event = libox.sandbox_lib_f(minetest.explode_scrollbar_event), - inventorycube = libox.sandbox_lib_f(minetest.inventorycube), - urlencode = libox.sandbox_lib_f(minetest.urlencode), - rgba = libox.sandbox_lib_f(minetest.rgba), - encode_base64 = libox.sandbox_lib_f(minetest.encode_base64), - decode_base64 = libox.sandbox_lib_f(minetest.decode_base64), - get_us_time = libox.sandbox_lib_f(minetest.get_us_time), - } -- safe minetest functions - env.core = env.minetest - - -- extra global environment stuff - for _, v in ipairs({ - "dump", "dump2" - }) do - env[v] = libox.sandbox_lib_f(_G[v]) - end - - -- oh yeah who could forget... - -- some random minetest stuffs - env.PcgRandom = libox.sandbox_lib_f(libox.safe.PcgRandom) - env.PerlinNoise = libox.sandbox_lib_f(libox.safe.PerlinNoise) - - env.traceback = libox.traceback - env.pat = { - find = libox.pat.find, - match = libox.pat.match, - gmatch = libox.pat.gmatch, - } - - libox.supply_additional_environment(env) -- for mods to use - return env -end diff --git a/mods/libox/env_docs.md b/mods/libox/env_docs.md deleted file mode 100644 index 24c1427f..00000000 --- a/mods/libox/env_docs.md +++ /dev/null @@ -1,99 +0,0 @@ -# The libox environment -- mods *will* extend this, there is nothing really useful that you can do with this if not extended - -# Globals (*) -- assert = unchanged -- error = unchanged -- collectgarbage(arg) = the only valid arg is `count`, may be changed idk -- ipairs = unchanged -- pairs = unchanged -- next = unchanged -- select = unchanged -- unpack = basically unchanged... -- pcall = libox.safe.pcall => pcall doesnt catch timeout errors -- xpcall = libox.safe.xpcall => xpcall doesnt catch timeout errors -- tonumber = unchanged -- tostring = unchanged -- type = unchanged -- loadstring(code) = libox.safe.get_loadstring(env) => does the lame sandboxing stuff like limiting environment, turning off JIT optimizations for the function, also you can't provide a chunkname -- _G = points back to the sandbox environment -- traceback = libox.traceback -- has_autohook = libox.has_autohook - -# String library (string.* or (""):* ) - -- byte = unchanged -- char = unchanged -- dump = unchanged -- find = forced to not match patterns -- format = unchanged, -- len = unchanged, -- lower = unchanged, -- rep = changed to not allow creating GIGANTIC strings, -- reverse = unchanged, -- sub = unchanged, -- upper = unchanged, -- trim = unchanged, from luanti -- split = forced to not match patterns, from luanti - -# Table library (table.*) - -- insert = unchanged -- maxn = unchanged -- remove = unchanged -- sort = unchanged -- indexof = unchanged, from luanti -- copy = unchanged, from luanti -- insert_all = unchanged, from luanti -- key_value_swap = unchanged, from luanti -- shuffle = unchanged, from luanti -- move = unchanged, LUAJIT ONLY -- concat = unchanged - -# Math library (math.*) - -- `abs`, `acos`, `asin`, `atan`, `atan2`, `ceil`, `cos`, `cosh`, `deg`, `exp`, `floor`, -- `fmod`, `frexp`, `huge`, `ldexp`, `log`, `log10`, `max`, `min`, `modf`, `pi`, `pow`, -- `rad`, `random`, `sin`, `sinh`, `sqrt`, `tan`, `tanh`, - -- `hypot`, `sign`, `factorial`, `round` - from luanti - -# Bit library (bit.*) -- Copied, unchanged - -# Vector library (vector.*) -- Copied, removed `vector.metatable` - -# os library (os.*) - -- clock = unchanged, -- datetable = from luacontroller, a date table..., -- difftime = uncahnged, -- time = unchanged, -- date = mooncontroller's safe_date, made to prevent segfaults, - - -# luanti library (core.* or minetest.*) - -- formspec_escape = unchanged*, -- explode_table_event = unchanged*, -- explode_textlist_event = unchanged*, -- explode_scrollbar_event = unchanged*, -- inventorycube = unchanged*, -- urlencode = unchanged*, -- rgba = unchanged*, -- encode_base64 = unchanged*, -- decode_base64 = unchanged*, -- get_us_time = unchanged*, - -\* All of theese functions fail when you give them gigantic string inputs - -# pattern library (pat.*) -- Pure lua implementations of functions `find`, `match`, `gmatch`, and they support patterns -# extra environment stuffs (*) mostly from luanti -- `dump` `dump2` = unchanged -- `PcgRandom` = you are given an interface, where `rand_normal_dist` has limited amount of tries and you call the functions by doing `my_random.func` not `my_random:func` -- `PerlinNoise` = Changed to give an interface, call it like `my_perlin.func` not `my_perlin:func`, also the types in the noiseparams are strict, if it detects a type mismatch it will give you 2 values: `false` and a string, that string being the faulty element (its shallow) - -# notes to mod devs -- Don't be afraid to use userdata, but be afraid where it can get to, it should not be serialized, never. \ No newline at end of file diff --git a/mods/libox/init.lua b/mods/libox/init.lua deleted file mode 100644 index 7dac51b3..00000000 --- a/mods/libox/init.lua +++ /dev/null @@ -1,82 +0,0 @@ -local ie = minetest.request_insecure_environment() -if ie == nil and (debug.getupvalue == nil and debug.getlocal == nil) then - minetest.log("warning", [[ -====Hello, this message is for server owners==== -Libox needs to be a trusted mod for weighing of coroutine sandboxes to work properly. -If it isn't, coroutine sandboxes could fill up your server's memory with local variables and upvalues. - -Libox can re-use debug.getlocal and debug.getupvalue if it is already avaliable in the environment -When adding libox to secure.trusted_mods, be aware that it will expose debug.getlocal and debug.getupvalue -================================================ -]]) -elseif debug.getlocal == nil or debug.getupvalue == nil and ie ~= nil then - -- luacheck:ignore - debug.getlocal = ie.debug.getlocal - debug.getupvalue = ie.debug.getupvalue -end - -if ie and jit then - local MP = core.get_modpath("libox") - local lib - if jit.os == 'Windows' then - lib = MP .. "/autohook/libautohook.dll" - else -- Linux/Unix-like - lib = MP .. "/autohook/libautohook.so" - end - -- Use package.loadlib() instad of require() to prevent using - -- system/other apps' libraries with the same name - local errmsg - libox_autohook_module, errmsg = ie.package.loadlib(lib, 'luaopen_autohook')() - while true do - if not libox_autohook_module or errmsg then - core.log("error", ('[libox] Autohook feature NOT available. Failed loading autohook C module %s:\n%s'):format(lib, errmsg)) - libox_autohook_module = nil - break - end - - local version_file = io.open(MP .. "/autohook/module-version.txt") - local module_version = libox_autohook_module.version - if not version_file or not module_version then - core.log("error", '[libox] Autohook feature NOT available, could not verify its C module compatibility') - libox_autohook_module = nil - break - end - local current_version = version_file:read("*l") - version_file:close() - - local module_version, jit_version = module_version() - if current_version ~= module_version then - core.log("error", '[libox] Autohook feature NOT available, its C module version is incompatible\n' - ..('current: %s | available: %s'):format(current_version, module_version)) - libox_autohook_module = nil - break - end - - if jit_version ~= jit.version then - core.log("error", '[libox] Autohook feature NOT available, its compiled against a different LuaJIT version than Luanti\n' - ..('Luanti: %s | autohook: %s'):format(jit.version, jit_version)) - libox_autohook_module = nil - end - - core.log("info", '[libox] Autohook feature available') - break - end -else - core.log("info", '[libox] Autohook feature NOT available') -end - -local MP = minetest.get_modpath(minetest.get_current_modname()) -dofile(MP .. "/main.lua") - --- Files that are executed sync only, coroutine.lua and *.test.lua -dofile(MP .. "/coroutine.lua") - -libox_autohook_module = nil - --- async files -minetest.register_async_dofile(MP .. "/main.lua") - -local test = MP .. "/test" -dofile(test .. "/basic_testing.lua") -dofile(test .. "/coroutine.test.lua") -dofile(test .. "/normal.test.lua") \ No newline at end of file diff --git a/mods/libox/main.lua b/mods/libox/main.lua deleted file mode 100644 index 36d4ed30..00000000 --- a/mods/libox/main.lua +++ /dev/null @@ -1,18 +0,0 @@ -libox = { - safe = {}, - supply_additional_environment = function(...) return ... end, -- for other mods to do their stuff - default_hook_time = 20, - default_time_limit = 3000, -- 3ms - disabled = false, - in_sandbox = false, -} - -if libox_autohook_module then - libox.has_autohook = true -end - -local MP = minetest.get_modpath("libox") -dofile(MP .. "/env.lua") -dofile(MP .. "/utils.lua") -dofile(MP .. "/normal.lua") -libox.pat = loadfile(MP .. "/pat.lua")() diff --git a/mods/libox/mod.conf b/mods/libox/mod.conf deleted file mode 100644 index bfb6c237..00000000 --- a/mods/libox/mod.conf +++ /dev/null @@ -1,6 +0,0 @@ -title = Libox -name = libox -description = A sandboxing library -optional_depends = dbg -min_minetest_version = 5.8 -supported_games = * \ No newline at end of file diff --git a/mods/libox/normal.lua b/mods/libox/normal.lua deleted file mode 100644 index 7f9ec048..00000000 --- a/mods/libox/normal.lua +++ /dev/null @@ -1,51 +0,0 @@ -local BYTECODE_CHAR = 27 - - -function libox.normal_sandbox(def) - if libox.disabled then - return false, "Libox is disabled. Please wait until the server admins re-enable it." - end - - local code = def.code - local env = def.env - local error_handler = def.error_handler or libox.unsafe_traceback - local in_hook = def.in_hook or libox.get_default_hook(def.max_time) - local function_wrap = def.function_wrap or function(f) return f end - - if code:byte(1) == BYTECODE_CHAR then - return false, "Bytecode is not allowed." -- mod security prevents it anyway, just making sure - end - - local f, msg = loadstring(code, "(libox sandbox: normal)") - if not f then return nil, msg end - setfenv(f, env) - - - if rawget(_G, "jit") then - jit.off(f, true) -- turn jit off for that function and yes this is needed or the user can `repeat until false`, sorry - end - - f = function_wrap(f) - - --local old_hook = { debug.gethook() } - - local ok, ret = false, "Something wrong happened" - - pcall(function() -- this is cursed because of a cursed bug - debug.sethook(in_hook, "", def.hook_time or libox.default_hook_time) - getmetatable("").__index = env.string - ok, ret = xpcall(f, function(...) - debug.sethook() -- try to fix a potential bug where someone can trigger a debug hook at just the right time for luanti to crash - return error_handler(...) - end) - debug.sethook() - getmetatable("").__index = string - end) - debug.sethook() - getmetatable("").__index = string -- OK HOPEFULLY FIX THAT BUG - if not ok then - return false, ret - else - return true, ret - end -end diff --git a/mods/libox/pat.lua b/mods/libox/pat.lua deleted file mode 100644 index a5661bda..00000000 --- a/mods/libox/pat.lua +++ /dev/null @@ -1,484 +0,0 @@ ---[========================================================================[-- - - Lua pattern matching library (minus string.gsub) ported to Lua. - - Copyright Ā© 1994–2018 Lua.org, PUC-Rio. - Copyright Ā© 2019 Pedro Gimeno Fortea. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - --]========================================================================] --- - -local LUA_MAXCAPTURES = 32 - -local CAP_UNFINISHED = -1 -local CAP_POSITION = -2 - - -local L_ESC = 37 -- 37='%' --- only used for string.find(x, y, z, true) --- local SPECIALS = '^$*+?.([%-' - -local function LUA_QL(x) - return "'" .. x .. "'" -end - -local function check_capture(ms, l) - l = l - 48 -- 48='0' - if l < 1 or l > ms.level or ms.capture[l].len == CAP_UNFINISHED then - return error("invalid capture index") - end - return l -end - -local function capture_to_close(ms) - local level = ms.level - while level > 0 do - if ms.capture[level].len == CAP_UNFINISHED then - return level - end - level = level - 1 - end - return error("invalid pattern capture") -end - -local function classend(pat, p) - local cc = pat:byte(p) - p = p + 1 - if cc == L_ESC then - if (pat:byte(p) or 0) == 0 then - return error("malformed pattern (ends with " .. LUA_QL('%') .. ")") - end - return p + 1 - end - if cc == 91 then -- 91='[' - if pat:byte(p) == 94 then -- 94='^' - p = p + 1 - end - repeat -- look for a `]' - cc = pat:byte(p) or 0 - p = p + 1 - if cc == 0 then - return error("malformed pattern (missing " .. LUA_QL(']') .. ")") - end - if cc == L_ESC then - if (pat:byte(p) or 0) ~= 0 then - p = p + 1 - end - end - until pat:byte(p) == 93 -- 93=']' - return p + 1 - end - return p -end - -local function match_class(c, cl) - local u = cl - cl % 64 + cl % 32 -- upper case - local negate = cl == u -- true if uppercase - local res - if u == 65 then -- 65='A' - res = c >= 65 and c <= 90 or c >= 97 and c <= 122 -- 65='A', 90='Z', 97='a', 122='z' - elseif u == 67 then -- 67='C' - res = c < 32 or c == 127 - elseif u == 68 then -- 68='D' - res = c >= 48 and c <= 57 -- 48='0', 57='9' - elseif u == 76 then -- 76='L' - res = c >= 97 and c <= 122 -- 97='a', 122='z' - elseif u == 80 then -- 80='P' - res = c >= 33 and c <= 47 or c >= 58 and c <= 64 - or c >= 91 and c <= 96 or c >= 123 and c <= 126 - elseif u == 83 then -- 83='S' - res = c == 9 or c >= 10 and c <= 13 or c == 32 -- 9=HT, 10=LF, 13=CR, 32=' ' - elseif u == 85 then -- 85='U' - res = c >= 65 and c <= 90 - elseif u == 87 then -- 87='W' - res = c >= 65 and c <= 90 or c >= 97 and c <= 122 -- 65='A', 90='Z', 97='a', 122='z' - or c >= 48 and c <= 57 -- 48='0', 57='9' - elseif u == 88 then -- 88='X' - res = c >= 65 and c <= 70 or c >= 97 and c <= 102 -- 65='A', 70='F', 97='a', 102='f' - or c >= 48 and c <= 57 -- 48='0', 57='9' - elseif u == 90 then - res = c == 0 - else - return c == cl - end - return negate ~= res -end - -local function matchbracketclass(c, pat, p, ec) - local sig = true - p = p + 1 - if pat:byte(p) == 94 then -- 94='^' - sig = false - p = p + 1 - end - while p < ec do - local cc = pat:byte(p) - if cc == L_ESC then - p = p + 1 - if match_class(c, pat:byte(p)) then - return sig - end - elseif pat:byte(p + 1) == 45 and p + 2 < ec then -- 45='-' - p = p + 2 - if cc <= c and c <= pat:byte(p) then - return sig - end - elseif cc == c then - return sig - end - p = p + 1 - end - return not sig -end - -local function singlematch(c, pat, p, ep) - local cc = pat:byte(p) - if cc == 46 then -- 46='.' - return true - end - if cc == L_ESC then - return match_class(c, pat:byte(p + 1)) - end - if cc == 91 then -- 91='[' - return matchbracketclass(c, pat, p, ep - 1) - end - return cc == c -end - -local match - -local function matchbalance(ms, str, s, pat, p) - local b = pat:byte(p) - local e = pat:byte(p + 1) - if (b or 0) == 0 or (e or 0) == 0 then - return error("unbalanced pattern") - end - if str:byte(s) ~= b then - return false - end - local cont = 1 - s = s + 1 - while s < ms.src_end do - local cc = str:byte(s) - if cc == e then - cont = cont - 1 - if cont == 0 then - return s + 1 - end - elseif cc == b then - cont = cont + 1 - end - s = s + 1 - end - return false -end - -local function max_expand(ms, str, s, pat, p, ep) - local i = 0 -- counts maximum expand for item - while s + i < ms.src_end and singlematch(str:byte(s + i), pat, p, ep) do - i = i + 1 - end - while i >= 0 do - local res = match(ms, str, s + i, pat, ep + 1) - if res then return res end - i = i - 1 -- else it didn't match; reduce 1 repetition to try again - end - return false -end - -local function min_expand(ms, str, s, pat, p, ep) - repeat - local res = match(ms, str, s, pat, ep + 1) - if res then - return res - end - if s >= ms.src_end or not singlematch(str:byte(s), pat, p, ep) then - return false - end - s = s + 1 - until false -end - -local function start_capture(ms, str, s, pat, p, what) - local level = ms.level + 1 - if level > LUA_MAXCAPTURES then - return error("too many captures") - end - if not ms.capture[level] then - ms.capture[level] = {} - end - ms.capture[level].init = s - ms.capture[level].len = what - ms.level = level - local res = match(ms, str, s, pat, p) - if not res then - ms.level = ms.level - 1 - end - return res -end - -local function end_capture(ms, str, s, pat, p) - local l = capture_to_close(ms) - ms.capture[l].len = s - ms.capture[l].init - local res = match(ms, str, s, pat, p) - if not res then - ms.capture[l].len = CAP_UNFINISHED - end - return res -end - -local function substrcomp(str, start1, start2, len) - for i = start1, start1 + len - 1 do - if str:byte(i) ~= str:byte(i + (start2 - start1)) then - return false - end - end - return true -end - -local function match_capture(ms, str, s, l) - l = check_capture(ms, l) - local len = ms.capture[l].len - if ms.src_end - s >= len and substrcomp(str, ms.capture[l].init, s, len) then - return s + len - end - return false -end - --- this is local already, don't add 'local' or it will fail -match = function(ms, str, s, pat, p) - local cc = pat:byte(p) or 0 - if cc == 40 then -- 40='(' - -- start capture - if pat:byte(p + 1) == 41 then -- 41=')' - -- position capture - return start_capture(ms, str, s, pat, p + 2, CAP_POSITION) - end - return start_capture(ms, str, s, pat, p + 1, CAP_UNFINISHED) - end - if cc == 41 then -- 41=')' - -- end capture - return end_capture(ms, str, s, pat, p + 1) - end - if cc == L_ESC then - cc = pat:byte(p + 1) - if cc == 98 then -- 98='b' - -- balanced string - s = matchbalance(ms, str, s, pat, p + 2) - if not s then - return false - end - return match(ms, str, s, pat, p + 4) - end - if cc == 102 then -- 102='f' - -- frontier - p = p + 2 - if pat:byte(p) ~= 91 then -- 91='[' - return error("missing " .. LUA_QL('[') .. " after " - .. LUA_QL('%f') .. " in pattern") - end - local ep = classend(pat, p) - local previous = str:byte(s - 1) or 0 - if matchbracketclass(previous, pat, p, ep - 1) - or not matchbracketclass(str:byte(s) or 0, pat, p, ep - 1) - then - return false - end - return match(ms, str, s, pat, ep) - end - if cc >= 48 and cc <= 57 then -- 48='0', 57='9' - -- capture results (%0-%9)? - s = match_capture(ms, str, s, cc) - if not s then - return false - end - return match(ms, str, s, pat, p + 2) - end - cc = -1 -- don't match anything else until default - end - if cc == 0 then -- end of pattern - return s - end - if cc == 36 then -- 36='$' - if (pat:byte(p + 1) or 0) == 0 then -- is the `$' the last char in pattern? - return s == ms.src_end and s -- check end of string - end - -- else fall through to default - end - - -- default - local ep = classend(pat, p) - local m = s < ms.src_end and singlematch(str:byte(s), pat, p, ep) - cc = pat:byte(ep) or 0 - if cc == 63 then - if m then - local res = match(ms, str, s + 1, pat, ep + 1) - if res then - return res - end - end - return match(ms, str, s, pat, ep + 1) - end - if cc == 42 then -- 42='*' - -- 0 or more repetitions - return max_expand(ms, str, s, pat, p, ep) - end - if cc == 43 then -- 43='+' - -- 1 or more repetitions - return m and max_expand(ms, str, s + 1, pat, p, ep) - end - if cc == 45 then -- 45='-' - -- 0 or more repetitions (minimum) - return min_expand(ms, str, s, pat, p, ep) - end - return m and match(ms, str, s + 1, pat, ep) -end - --- lmemfind not implemented - only used for string.find(x, y, z, true) - -local function push_onecapture(ms, i, str, s, e) - if i > ms.level then - if i ~= 1 then - return error("invalid capture index") - end - ms.captures[#ms.captures + 1] = str:sub(s, e - 1) - else - local l = ms.capture[i].len - if l == CAP_UNFINISHED then - return error("unfinished capture") - end - if l == CAP_POSITION then - ms.captures[#ms.captures + 1] = ms.capture[i].init - else - ms.captures[#ms.captures + 1] = str:sub(ms.capture[i].init, ms.capture[i].init + l - 1) - end - end -end - -local function push_captures(ms, str, s, e) - local nlevels = ms.level - if nlevels == 0 and s then - nlevels = 1 - end - for i = 1, nlevels do - push_onecapture(ms, i, str, s, e) - end -end - -local function posrelat(pos, len) - if pos < 0 then pos = pos + len + 1 end - return pos >= 1 and pos or 1 -end - -local string_find = string.find - -local SPECIALS = { - [("^"):byte(1)] = true, - [("$"):byte(1)] = true, - [("*"):byte(1)] = true, - [("+"):byte(1)] = true, - [("?"):byte(1)] = true, - [("."):byte(1)] = true, - [("("):byte(1)] = true, - [("["):byte(1)] = true, - [("%"):byte(1)] = true, - [("-"):byte(1)] = true, - [0] = true -} - -local function specials_free(s) - for i = 1, #s do - if SPECIALS[s:byte(i)] then - if s:byte(i) == 0 then - return true -- stop at first NUL - end - return false - end - end - return true -end - -local function str_find_aux(find, str, pat, init, explicit) - local l1 = #str - init = posrelat(init or 1, l1) - if init < 1 then - init = 1 - elseif init > l1 + 1 then - init = l1 + 1 - end - if find and (explicit or specials_free(pat)) then -- explicit request? - -- do a plain search - return string_find(str, pat, init, true) - end - local p = 1 - local anchor = false - if pat:byte(1) == 94 then -- 94='^' - p = p + 1 - anchor = true - end - local s1 = init - local ms = { captures = {}, capture = {}, src_end = l1 + 1 } - repeat - ms.level = 0 - local res = match(ms, str, s1, pat, p) - if res then - if find then - push_captures(ms, str, false, 0) - return s1, res - 1, unpack(ms.captures) - end - push_captures(ms, str, s1, res) - return unpack(ms.captures) - end - s1 = s1 + 1 - until anchor or s1 > ms.src_end - return nil -end - -local function str_find(str, pat, init, explicit) - return str_find_aux(true, str, pat, init, explicit) -end - -local function str_match(str, pat, init) - return str_find_aux(false, str, pat, init) -end - -local function gmatch(str, pat) - local init = 1 - local ms = { captures = {}, capture = {}, src_init = str, src_end = #str + 1 } - local function gmatch_aux() - for i = 1, #ms.captures do ms.captures[i] = nil end - for src = init, ms.src_end do - ms.level = 0 - local e = match(ms, str, src, pat, 1) - if e then - local newstart = e - if e == src then newstart = newstart + 1 end -- empty match? go at least one position - init = newstart - push_captures(ms, str, src, e) - return unpack(ms.captures) - end - end - end - return gmatch_aux -end - - -return { find = str_find, match = str_match, gmatch = gmatch } diff --git a/mods/libox/settingtypes.txt b/mods/libox/settingtypes.txt deleted file mode 100644 index df47521a..00000000 --- a/mods/libox/settingtypes.txt +++ /dev/null @@ -1,24 +0,0 @@ -# Enables automatic coroutine sandbox garbage -# collection, this is only nessesary when: -# 1) You have a mod that does not clean up sandboxes after itself -# - Also, unloaded areas may be a huge issue -# 2) You have way too many sandboxes running -# is experimental -libox.gc.auto (Enable automatic garbage collection) bool false - - -# (In seconds) If a coroutine sandbox has been untouched -# for this long, garbage collection may remove it -libox.gc.time_treshold (Garbage collection time treshold) float 600 - - -# Garbage collection will start to -# actually do stuff when there are this -# many coroutine sandboxes -libox.gc.number_of_sandboxes (Garbage collection "number of sandboxes" treshold) int 60 - -# In seconds -# "How often should the gc be executed" -# Only works when Automatic gc is enabled -# (gc = garbage collection) -libox.gc.interval (Garbage collection interval) int 60 \ No newline at end of file diff --git a/mods/libox/test/basic_testing.lua b/mods/libox/test/basic_testing.lua deleted file mode 100644 index e3755e0c..00000000 --- a/mods/libox/test/basic_testing.lua +++ /dev/null @@ -1,81 +0,0 @@ --- this is a very crappy testing framework i came up with --- combine the best of mtt and mineunit i guess (mtt is able to test weird async easily, i love it) --- with some terrible code of course -local api = {} -local tests = {} - - - -local function core(test) - local function get_success(name) - return function() minetest.log(name .. " : success") end - end - local function get_failure(name) - return function() minetest.log(name .. " : failure") end - end - local function get_custom(name) - return function(str) minetest.log(name .. " : " .. str) end - end - - local function get_assert(name) - return function(cond) - if cond then - get_success(name)() - else - get_failure(name)() - end - end - end - - test[2](get_assert(test[1]), get_success(test[1]), get_failure(test[1]), get_custom(test[1])) -end - -function api.run_known_test(test) - if not test.ignore_async then - minetest.handle_async(core, function() end, test) - end - core(test) -end - -function api.run_test(name, name2) - local category = tests[name] - - local test - for _, v in ipairs(category) do - if v[1] == name2 then - test = name2 - break - end - end - assert(test, "Test not found") - api.run_known_test(test) -end - -function api.run_category(name) - local category = tests[name] - - assert(category, "Category not found") - for _, v in ipairs(category) do - api.run_known_test(v) - end -end - -function api.run_tests() - for k, _ in pairs(tests) do - minetest.log("========" .. k .. "========") - api.run_category(k) - end -end - -function api.get_it(name, ignore_async) -- create test - return function(name2, f) - tests[name][#tests[name] + 1] = { name2, f, ignore_async = ignore_async } -- preserve order - end -end - -function api.describe(name, f, ignore_async) -- create category - tests[name] = {} - f(api.get_it(name, ignore_async)) -end - -libox.test = api diff --git a/mods/libox/test/coroutine.test.lua b/mods/libox/test/coroutine.test.lua deleted file mode 100644 index 78b568c0..00000000 --- a/mods/libox/test/coroutine.test.lua +++ /dev/null @@ -1,273 +0,0 @@ -libox.test.describe("coroutine sandbox", function(it) - it("Can yield", function(assert) - local sandbox = libox.coroutine.create_sandbox({ - code = [[ -local count = 0 -repeat - count = count+1 - coroutine.yield(count) -until false -]], - env = { - coroutine = { - yield = coroutine.yield - } - }, - }) - - local values = { - select(2, libox.coroutine.run_sandbox(sandbox)), -- 1 - select(2, libox.coroutine.run_sandbox(sandbox)), -- 2 - select(2, libox.coroutine.run_sandbox(sandbox)), -- 3 - select(2, libox.coroutine.run_sandbox(sandbox)), -- 4 - } - - local succ = (values[1] == 1) and (values[2] == 2) - and (values[3] == 3) and (values[4] == 4) - if not succ then - minetest.debug(dump(values)) - end - assert(succ) - end) - - it("Limits time", function(assert) - local sandbox = libox.coroutine.create_sandbox({ - code = "repeat until false", - env = {}, - }) - local ok = libox.coroutine.run_sandbox(sandbox) - assert(not ok) - end) - - it("Limits environment size", function(assert) - local sandbox = libox.coroutine.create_sandbox({ - code = "a = string.rep('a',1000); coroutine.yield()", - env = { - string = { - rep = string.rep, - -- Never let the user get arbitrary string.rep, or the user can create terabytes of strings... - -- we are giving it to the user here for testing - }, - }, - size_limit = 1000 - - }) - local _, errmsg = libox.coroutine.run_sandbox(sandbox) - assert(errmsg == "Out of memory!") - end) - - it("Limits circular references", function(_, success) - local env = {} - env._G = env - env.f = function() - --local a = env._G - return env._G - end - local sandbox = libox.coroutine.create_sandbox({ - code = [[ - local e = _G -- Hehe i am an unsuspecting user and i am sure this won't be a mistake - coroutine.yield() - ]], - -- When having _G as the environment - -- it simply stack overflows? this is like... bad... yeah - -- old comment, i fixed that actually, see down - env = env, - size_limit = 1000, - time_limit = 1000000 -- 1 million microseconds, can you imagine that (that's definitely not 1 second) - - }) - - libox.coroutine.run_sandbox(sandbox) - success() - end) - - it("Limits local variables", function(assert) - --[[ - IMPORTANT: This fails when there is no access to debug.getlocal - ]] - - local sandbox = libox.coroutine.create_sandbox({ - code = "local a = string.rep('a',1000); coroutine.yield(); coroutine.yield()", - env = { - string = { - rep = string.rep, - }, - coroutine = { - yield = coroutine.yield - }, - }, - size_limit = 1000, - time_limit = 1000000, - - }) - local _, errmsg = libox.coroutine.run_sandbox(sandbox) - assert(errmsg == "Out of memory!") - end) - - it("Really limits local variables...", function(assert) - --[[ - This fails when there is no access to debug.getlocal - ]] - - local sandbox = libox.coroutine.create_sandbox({ - code = [[ - function evil() - local a = string.rep('a',1000) - coroutine.yield(); - end - evil(); - coroutine.yield(); - coroutine.yield() - ]], - env = { - string = { - rep = string.rep, - }, - coroutine = { - yield = coroutine.yield - }, - }, - size_limit = 1000, - time_limit = 1000000 - - }) - local _, errmsg = libox.coroutine.run_sandbox(sandbox) - assert(errmsg == "Out of memory!") - end) - - it("Limits upvalues", function(assert) - --[[ - this fails when we cant look up upvalues - ]] - local sandbox = libox.coroutine.create_sandbox({ - code = [[ - function get_evil() - local evil = string.rep('a', 60000000) - return function() - return evil - end - end - local evil = get_evil() - coroutine.yield() - ]], - env = { - string = { - rep = string.rep, - }, - coroutine = { - yield = coroutine.yield - }, - }, - size_limit = 1000, - time_limit = 1000000 - - }) - local _, errmsg = libox.coroutine.run_sandbox(sandbox) - assert(errmsg == "Out of memory!") - end) - - it( - "Weighing _G", - function(_, _, _, custom) - local env = libox.create_basic_environment() - libox.coroutine.get_size(env, {}, coroutine.create(function() end)) - -- JIT it up a little, idk man - - local sandbox = libox.coroutine.create_sandbox({ - code = [[ - local e = _G - coroutine.yield() - ]], - -- yeah, it used to stack overflow but now with a minor rewrite it doesn't - env = _G, - size_limit = 1000, - time_limit = 1000000 - }) - - local t1 = minetest.get_us_time() - libox.coroutine.run_sandbox(sandbox) - local sandboxd = libox.coroutine.active_sandboxes[sandbox] - custom("time:" .. - (minetest.get_us_time() - t1) / 1000 .. "ms" .. - " size:" .. libox.coroutine.get_size(sandboxd.env, {}, sandboxd.thread) - .. - " digiline sanitize thinks:" .. - ({ libox.digiline_sanitize(env, true) })[2] .. " lua gc thinks: " .. collectgarbage("count") * 1024 - ) - end) - it("Can detect if sandbox is dead", function(assert) - local sandbox = libox.coroutine.create_sandbox({ - code = "return", - env = {}, - size_limit = 1000, - time_limit = 1000 - }) - libox.coroutine.run_sandbox(sandbox) - assert(libox.coroutine.is_sandbox_dead(sandbox)) - end) - it("Can collect garbage", function(_, _, _, custom) - local sandboxes = {} - local num = 50 - local activation_treshold = 10 - local env = libox.create_basic_environment() - local fake_time_treshold = 50 - - local response = "\n" - - response = response .. - "Size of environment: " - .. libox.coroutine.get_size(env, {}, coroutine.create(function() end), false) .. "\n" - response = response .. "Number of sandboxes that will get created:" .. num .. "\n" - response = response .. "Activation treshold (number of sandboxes):" .. activation_treshold .. "\n" - response = response .. "Activation treshold (time, faked):" .. fake_time_treshold .. "s \n" - - local active = libox.coroutine.active_sandboxes - - local function map_bool(n) - if n == 1 then return true else return false end - end - local function funny_if(c, t, f) - if c then return t else return f end - end - - for i = 1, num do - sandboxes[i] = libox.coroutine.create_sandbox({ - code = "coroutine.yield()", - env = env, - size_limit = 10000, - time_limit = 10000, - is_garbage_collected = map_bool(math.random(0, 1)) - }) - - local actual_sandbox = active[sandboxes[i]] - actual_sandbox.last_ran = funny_if(math.random() < 0.5, 0, os.clock()) - end - - - local function dumbcount(t) - local count = 0 - for _ in pairs(t) do count = count + 1 end - return count - end - - response = response .. "number of sandboxes currently: " .. dumbcount(active) .. "\n" - - local original_settings = table.copy(libox.coroutine.settings) - - libox.coroutine.settings.gc.time_treshold = fake_time_treshold - libox.coroutine.settings.gc.number_of_sandboxes = activation_treshold - - libox.coroutine.garbage_collect() - - libox.coroutine.settings = original_settings - - response = response .. - "number of sandboxes after collection (it should remain high because their 'state' is random): " .. - dumbcount(active) .. "\n" - - - libox.coroutine.active_sandboxes = {} - response = response .. "Cleared the mess" - custom(response) - end) -end, true) diff --git a/mods/libox/test/normal.test.lua b/mods/libox/test/normal.test.lua deleted file mode 100644 index 4bbe2d09..00000000 --- a/mods/libox/test/normal.test.lua +++ /dev/null @@ -1,227 +0,0 @@ -libox.test.describe("Normal sandbox (tests the environment, mostly)", function(it) - it("Doesn't do bytecode", function(assert) - -- mod security prevents it anyway - assert(not libox.normal_sandbox({ - code = string.dump(assert), - env = {}, - in_hook = function() end, - })) - end) - it("Limits time", function(assert) - assert(not libox.normal_sandbox({ - code = "repeat until false ", - env = {}, - max_time = 1000 - })) - end) - it("Handles pcall correctly", function(assert) - assert(not libox.normal_sandbox({ - code = "pcall(function() repeat until false end)", - env = libox.create_basic_environment(), - max_time = 1000 - })) - end) - it("Handles xpcall correctly", function(assert) - assert(not libox.normal_sandbox({ - code = "xpcall(function() error('yeh') end,function() repeat until false end)", - env = libox.create_basic_environment(), - max_time = 1000 - })) - end) - it("Isn't vurnable to severe trollery", function(_, _, bad, custom) - local t1 = minetest.get_us_time() - local ok, err = libox.normal_sandbox({ - code = [[ - local x = "." - repeat - x = x .. x - until false - ]], - env = {}, - max_time = 10000, -- 10 milis for this - }) - local t2 = minetest.get_us_time() - if ok then - bad() - else - custom("took " .. (t2 - t1) .. "us, sandbox was given 10 000 us, for debug: the error was: " .. dump(err)) - end - end) - it("Can loadstring", function(assert) - assert(libox.normal_sandbox({ - code = [[ - assert( - loadstring( - "return 'hi'" - ) - ) - ]], - env = libox.create_basic_environment(), - max_time = 1000, - })) - end) - it("Can loadstring securely", function(assert) - assert(not libox.normal_sandbox({ - code = [[ - loadstring('assert(debug)')() - -- we don't use minetest as an example - -- because libox.create_basic_environment already creates a global with that name - ]], - env = libox.create_basic_environment(), - max_time = 1000, - })) - end) - it("Can handle weird recursive shenanigans (that mostly exploit traceback)", function(_, _, bad, custom) - --[[ - This attempts to abuse libox.traceback to create a gigantic - error message and force several executions of debug.getinfo - - (Now fixed, the limit is 20 debug.getinfo's before just stopping) - ]] - local code = [[ - pcall(loadstring(code)()) - ]] - local env = libox.create_basic_environment() - env.code = code - - local t1 = minetest.get_us_time() - local ok, _ = libox.normal_sandbox({ - code = code, - env = env, - max_time = 10000 - }) - local t2 = minetest.get_us_time() - if ok then - bad() - else - custom("took: " .. (t2 - t1) .. "us, sandbox was given 10 000 us") - end - end) - it("Can't abuse string.rep", function(assert, _, _, _) - assert(not libox.normal_sandbox({ - code = "string.rep('a',9999999)", - env = libox.create_basic_environment(), - max_time = 10000, - })) - end) - it("Can limit string length on functions that probably need it", function(assert) - assert(not libox.normal_sandbox({ - code = [[ - local str = string.rep(":",64000) - str = str .. str - minetest.urlencode(str) - ]], - max_time = 10000, - env = libox.create_basic_environment() - })) - end) - - it("Can check types (basic)", function(assert) - local type_string = function(x) return type(x) == "string" end - local result1 = libox.type_check({ - a = "lol", - b = { - c = "lol" - } - }, { - a = type_string, - b = { - c = type_string - } - }) == true - - local result2 = libox.type_check({ - a = "lol", - b = { - c = "lol", - d = "funny (i am not supposed to be here)", - } - }, { - a = type_string, - b = { - c = type_string - } - }) == false - - local result3 = libox.type_check({ - a = "fine", - b = "fine also", - c = ItemStack(""), -- not fine - d = {} - }, { - a = type_string, - b = type_string, - c = type_string, - d = type_string, - }) == false - - local result4 = libox.type_check("moo", type_string) == true - local result5 = libox.type_check("b", { - a = type_string - }) == false - - local result7 = not libox.type_check({ - a = "b", - }, { - a = type_string, - b = type_string, - c = { - a = type_string, - b = type_string, - } - }) - - local result6 = libox.type_check({ - a = ItemStack("") - }, { - a = type_string, - }) == false - - assert(result1 and result2 and result3 and result4 and result5 and result6 and result7) - end) - it("Can check types (recursive)", function(assert) - local table = { - f = function() end - } - table["k"] = table - assert(not libox.type_check(table, { - f = function(x) return type(x) == "function" end, - k = { - x = function(_) return "lol" end - } - })) - end) - - it("Userdata propertly type checked - PerlinNoise", function(assert) - local perlin = libox.safe.PerlinNoise - debug.sethook(function() end, "", 1000) -- make it so sandbox_lib_f wont get mad - local result1 = perlin({ - offset = 0, - scale = 1, - spread = { x = 384, y = 192, z = 384 }, - seed = 5900033, - octaves = 5, - persist = 0.63, - lacunarity = 2.0, - --flags = "" - }) ~= false - local result2 = perlin({}) == false - local result3 = perlin({ - offset = 0, - scale = 1, - spread = { x = 384, y = 192, z = 384 }, - seed = 5900033, - octaves = 50, - persist = 0.63, - lacunarity = 50.0, - --flags = "" - }) ~= false - --[[ - So, about octaves/lacunarity - when i make those values huge, nothing really seems to happen other than timeout - i think its fine tbh - ]] - debug.sethook() - assert(result1 and result2 and result3) - end) -end) diff --git a/mods/libox/utils.lua b/mods/libox/utils.lua deleted file mode 100644 index fae1ba20..00000000 --- a/mods/libox/utils.lua +++ /dev/null @@ -1,282 +0,0 @@ -function libox.get_default_hook(max_time) - local time = minetest.get_us_time - - local start_time = time() - return function() - if time() - start_time > max_time then - debug.sethook() - - error("Code timed out! Reason: Time limit exceeded, the limit:" .. - tostring(max_time / 1000) .. "ms, the program took:" .. ((time() - start_time) / 1000), 2) - end - end -end - --- luacheck: push ignore - ---[[ - PATH SHORTENING: from dbg - Path shortening is licensed under the MIT license: - - Copyright 2022 Lars Müller - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, - sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -]] - --- luacheck: pop ignore - -local modpath_trie = {} -for _, modname in pairs(minetest.get_modnames()) do - local path = minetest.get_modpath(modname) - local subtrie = modpath_trie - for char in path:gmatch "." do - subtrie[char] = subtrie[char] or {} - subtrie = subtrie[char] - end - subtrie["\\"] = modname - subtrie["/"] = modname -end - -function libox.shorten_path(path) - -- Search for a prefix (paths have at most one prefix) - local subtrie = modpath_trie - for i = 1, #path do - if type(subtrie) == "string" then - return subtrie .. ":" .. path:sub(i) - end - subtrie = subtrie[path:sub(i, i)] - if not subtrie then return path end - end - return path -end - --- PATH SHORTENING END, rest is licensed as usual - -if minetest.global_exists("dbg") then - libox.shorten_path = dbg.shorten_path -end - -local TRACEBACK_LIMIT = 20 - -function libox.traceback(errmsg) - errmsg = tostring(errmsg) or "" - - local traceback = "Traceback: " .. "\n" - local level = 1 - - while level < TRACEBACK_LIMIT do - local info = debug.getinfo(level, "nlS") -- can be quite slow actually, thats why TRACEBACK_LIMIT is in place - if not info then break end - local name = info.name - local text - if name ~= nil then - text = "In function " .. name - else - text = "In " .. info.what - end - if info.source == "=(load)" then - traceback = traceback .. text .. " at line " .. info.currentline .. "\n" - end - level = level + 1 - end - - if level == TRACEBACK_LIMIT then traceback = traceback .. "\n... and more" end - return libox.shorten_path(errmsg) .. "\n" .. traceback -end - --- dont rely on this to sethook and -function libox.unsafe_traceback(errmsg) - debug.sethook() - getmetatable("").__index = string - return libox.traceback(errmsg) -end - -function libox.digiline_sanitize(input, allow_functions, wrap) - --[[ - Parameters: - 1) input: the thing - 2) allow_functions: true/false, explains itself - 3) wrap: function, the function that wraps around the functions in this table - ]] - - wrap = wrap or function(f) return f end - local function clean_and_weigh_digiline_message(msg, back_references) - local t = type(msg) - if t == "string" then - -- Strings are immutable so can be passed by reference, and cost their - -- length plus the size of the Lua object header (24 bytes on a 64-bit - -- platform) plus one byte for the NUL terminator. - return msg, #msg + 25 - elseif t == "number" then - -- Numbers are passed by value so need not be touched, and cost 8 bytes - -- as all numbers in Lua are doubles. NaN values are removed. - if msg ~= msg then - return nil, 0 - end - return msg, 8 - elseif t == "boolean" then - -- Booleans are passed by value so need not be touched, and cost 1 - -- byte. - return msg, 1 - elseif t == "table" then - -- Tables are duplicated. Check if this table has been seen before - -- (self-referential or shared table); if so, reuse the cleaned value - -- of the previous occurrence, maintaining table topology and avoiding - -- infinite recursion, and charge zero bytes for this as the object has - -- already been counted. - back_references = back_references or {} - local bref = back_references[msg] - if bref then - return bref, 0 - end - -- Construct a new table by cleaning all the keys and values and adding - -- up their costs, plus 8 bytes as a rough estimate of table overhead. - local cost = 8 - local ret = {} - back_references[msg] = ret - for k, v in pairs(msg) do - local k_cost, v_cost - k, k_cost = clean_and_weigh_digiline_message(k, back_references) - v, v_cost = clean_and_weigh_digiline_message(v, back_references) - if k ~= nil and v ~= nil then - -- Only include an element if its key and value are of legal - -- types. - ret[k] = v - end - -- If we only counted the cost of a table element when we actually - -- used it, we would be vulnerable to the following attack: - -- 1. Construct a huge table (too large to pass the cost limit). - -- 2. Insert it somewhere in a table, with a function as a key. - -- 3. Insert it somewhere in another table, with a number as a key. - -- 4. The first occurrence doesn’t pay the cost because functions - -- are stripped and therefore the element is dropped. - -- 5. The second occurrence doesn’t pay the cost because it’s in - -- back_references. - -- By counting the costs regardless of whether the objects will be - -- included, we avoid this attack; it may overestimate the cost of - -- some messages, but only those that won’t be delivered intact - -- anyway because they contain illegal object types. - cost = cost + k_cost + v_cost - end - return ret, cost - elseif t == "function" and allow_functions then - local success, bytecode = pcall(function() - return string.dump(msg) - end) - if not success then - return nil, 0 - else - return wrap(msg), #bytecode + 25 - end - else - return nil, 0 - end - end - - - local msg, cost = clean_and_weigh_digiline_message(input) - if not msg then - return nil, 0 - else - return msg, cost - end -end - -function libox.sandbox_lib_f(f, opt_str_limit) - --[[ - Sandbox external functions, call this on functions that - - don't run user code - - use "":sub(1, 2, whatever) syntax and that syntax is critical or use pcall or xpcall - - get laggy when supplied with gigantic sting inputs - - ]] - return function(...) - local args = { ... } - for _, v in pairs(args) do - if type(v) == "string" and #v > (opt_str_limit or 64000) then error("String too large", 2) end - end - - local string_meta = getmetatable("") - local sandbox = string_meta.__index - - string_meta.__index = string - local retvalue = { f(...) } - string_meta.__index = sandbox - - if not debug.gethook() then - error("Code timed out! (Reason: external function erased the debug hook)", 2) - end - return unpack(retvalue) - end -end - -libox.safe_traceback = libox.sandbox_lib_f(libox.safe_traceback) -- make it work - - --- strict type checking ---[[ - thing: any - type_check: { - key = function | table - } | function - note: recursive - returns: boolean - -]] -function libox.type_check(initial_thing, initial_check) - local function internal(thing, check, seen) - if seen[thing] == true then return true end - if type(check) == "function" then - seen[thing] = true - return check(thing) - end - - for k, v in pairs(check) do - if thing[k] == nil then - if type(v) == "function" then - if v(nil) == false then - return false, k - end - else - return false - end - elseif type(v) == "table" then - if (internal(thing[k], v, seen)) == false then return false, k end - seen[thing[k]] = true - elseif type(v) == "function" then - if v(thing[k]) == false then return false, k end - seen[thing[k]] = true - else -- bad, just return false - return false, "invalid params to initial_check probably" - end - end - for k, _ in pairs(thing) do - if check[k] == nil then return false, "un-needed: " .. k end - end - return true - end - return internal(initial_thing, initial_check, {}) -end - -function libox.type(x) - return function(something) - return type(something) == x - end -end - -function libox.type_vector(vec) - if type(vec) ~= "table" then return false end - - if type(vec.x) ~= "number" then return false end - if type(vec.y) ~= "number" then return false end - if type(vec.z) ~= "number" then return false end - return true -end diff --git a/mods/minetest-fmod b/mods/minetest-fmod new file mode 160000 index 00000000..b414049b --- /dev/null +++ b/mods/minetest-fmod @@ -0,0 +1 @@ +Subproject commit b414049bbb6356bc8d21740899225b3dfb328efe diff --git a/mods/minetest-futil b/mods/minetest-futil new file mode 160000 index 00000000..b7ed1ebb --- /dev/null +++ b/mods/minetest-futil @@ -0,0 +1 @@ +Subproject commit b7ed1ebbb7e5ab5bd77706479e905571855583b7 diff --git a/mods/moblib/Readme.md b/mods/moblib/Readme.md deleted file mode 100644 index 84325757..00000000 --- a/mods/moblib/Readme.md +++ /dev/null @@ -1,51 +0,0 @@ -# Entity Library (`moblib`) - -Low-level high-performance entity library - -## About - -Depends on [`modlib`](https://github.com/appgurueu/modlib). Licensed under the MIT License. Written by Lars Mueller aka LMD or appguru(eu). - -## Links - -* [GitHub](https://github.com/appgurueu/moblib) - sources, issue tracking, contributing -* [Discord](https://discordapp.com/invite/ysP74by) - discussion, chatting -* [Minetest Forum](https://forum.minetest.net/viewtopic.php?t=24671) - (more organized) discussion -* [ContentDB](https://content.minetest.net/packages/LMD/moblib) - releases (cloning from GitHub is recommended) - -## API - -Mostly self-documenting code. Mod namespace is `moblib`, containing all variables & functions. - -### `vector get_rotation(vector direction)` - -Returns rotation required to rotate a z-facing model in direction. - -### `vector get_wield_rotation(vector direction)` - -Same as `get_rotation` but for wield_images. - -### `vector get_direction(vector rotation)` - -Inverse of `get_rotation`. - -### `register_entity(name, def)` - -### `register_entity(text name, table def)` - -```lua -moblib.register_entity(name, { - initial_properties = {...}, - lua_properties = { - moveresult = { - collisions = nil, - axes = nil, - old_velocity = nil, - acceleration_dependent = nil - }, - staticdata = "json" or "lua" - }, - on_step = ..., - ... -}) -``` \ No newline at end of file diff --git a/mods/moblib/init.lua b/mods/moblib/init.lua deleted file mode 100644 index 6758f808..00000000 --- a/mods/moblib/init.lua +++ /dev/null @@ -1 +0,0 @@ -modlib.mod.init() \ No newline at end of file diff --git a/mods/moblib/main.lua b/mods/moblib/main.lua deleted file mode 100644 index c7403899..00000000 --- a/mods/moblib/main.lua +++ /dev/null @@ -1,326 +0,0 @@ -storage = minetest.get_mod_storage() -entities_by_id = setmetatable({}, {__mode = "v"}) -objects_by_id = setmetatable({}, { - __index = function(_, id) - local entity = entities_by_id[id] - return entity and entity.object - end, - __newindex = function(self, id, object) - local luaentity = object:get_luaentity() - if luaentity then - entities_by_id[id] = luaentity - else - self[id] = object - end - end, - __mode = "v" -}) - -local highest_id = storage:get_int("highest_id") - -minetest.register_on_joinplayer(function(player) - -- INFO no need to check whether it's an entity - rawset(objects_by_id, player:get_player_name(), player) -end) - -minetest.register_on_leaveplayer(function(player) - objects_by_id[player:get_player_name()] = nil -end) - -function get_id(object) - if object:is_player() then - return object:get_player_name() - end - local luaentity = object:get_luaentity() - if luaentity and luaentity._ then - return luaentity._.id - end -end - -function get_object(id) - return objects_by_id[id] -end - -function get_entity(id) - return entities_by_id[id] -end - --- x/z-rotation -local function horizontal_rotation(direction) - return math.atan2(direction.y, math.sqrt(direction.x*direction.x + direction.z*direction.z)) -end - --- y-rotation -local function vertical_rotation(direction) - return -math.atan2(direction.x, direction.z) -end - --- gets rotation in radians for a z-facing object -function get_rotation(direction) - return { - x = horizontal_rotation(direction), - y = vertical_rotation(direction), - z = 0 - } -end - --- converts a rotation from -pi to pi to 2pi to 0 -function convert_rotation(rotation) - return vector.apply(rotation, function(c) - if c < 0 then - return 2*math.pi + c - end - return c - end) -end - --- shorthand -function get_converted_rotation(direction) - return convert_rotation(get_rotation(direction)) -end - --- normalizes a rotation -function normalize_rotation(rotation) - return vector.apply(rotation, function(c) - local nc = c % (2*math.pi) - if c < 0 then - return 2*math.pi + nc - end - return nc - end) -end - -function get_minimum_converted_rotation_difference(rotation, other_rotation) - return vector.apply(vector.subtract(rotation, other_rotation), function(c) - if c > math.pi then - return -2*math.pi + c - end - if c < -math.pi then - return 2*math.pi + c - end - return c - end) -end - --- gets rotation in radians for a wielditem (such as a sword) -function get_wield_rotation(direction) - return { - x = 0, - y = 1.5*math.pi+vertical_rotation(direction), - z = 1.25*math.pi+horizontal_rotation(direction) - } -end - --- gets the direction for a rotated vector (0, 0, 1), inverse of get_rotation -function get_direction(rotation) - local rx, ry = rotation.x, rotation.y - local direction = {} - -- x rotation - direction.y = math.sin(rx) - local z = math.cos(rx) - -- y rotation - direction.x = -(z * math.sin(ry)) - direction.z = z * math.cos(ry) - return direction -end - -function set_look_dir(player, direction) - local rotation = get_rotation(direction) - player:set_look_vertical(-rotation.x) - player:set_look_horizontal(rotation.y) -end - -function get_eye_pos(object) - local eye_pos = object:get_pos() - if object:is_player() then - eye_pos.y = eye_pos.y + object:get_properties().eye_height - end - return eye_pos -end - -function get_center(object) - local collisionbox = object:get_properties().collisionbox - return vector.add(object:get_pos(), vector.divide(vector.add(vector.new(collisionbox[1], collisionbox[2], collisionbox[3]), vector.new(unpack(collisionbox, 4))), 2)) -end - -function get_mass(object) - local entity = object:get_luaentity() - if entity and entity._mass then - return entity._mass - end - local collisionbox = object:get_properties().collisionbox - local mass = (collisionbox[4] - collisionbox[1]) * (collisionbox[5] - collisionbox[2]) * (collisionbox[6] - collisionbox[3]) - assert(mass > 0) - return mass -end - -function calculate_damage(object, time_since_last_punch, caps) - local damage = 0 - local armor_groups = assert(object:get_armor_groups()) -- object has to be alive - for group, group_damage in pairs(caps.damage_groups) do - damage = damage + group_damage * (armor_groups[group] or 0) / 100 - end - return damage * math.min(1, math.max(0, time_since_last_punch / caps.full_punch_interval)) -end - --- TODO implement physics such as air resistance -local engine_has_moveresult = minetest.has_feature("object_step_has_moveresult") -local sensitivity = 0.01 -function register_entity(name, def) - local props = def.lua_properties - def.lua_properties = nil - local on_activate = def.on_activate or function() end - local on_step = def.on_step or function() end - local terminal_speed = props.terminal_speed - if terminal_speed then - local old_on_step = on_step - function on_step(self, dtime, ...) - old_on_step(self, dtime, ...) - local obj = self.object - local vel = obj:get_velocity() - if not vel then return end -- object has been deleted - local len = vector.length(obj:get_velocity()) - if len > terminal_speed then - obj:set_velocity(vector.multiply(vector.divide(vel, len))) - end - end - end - local props_staticdata = props.staticdata - local props_id = props.id - if props_id then - assert(props_staticdata) - end - if props_staticdata then - local implementation - if type(props_staticdata) == "table" then - implementation = props_staticdata - else - implementation = ({ - json = { - serializer = minetest.write_json, - deserializer = minetest.parse_json - }, - lua = { - serializer = minetest.serialize, - deserializer = minetest.deserialize - } - })[props_staticdata] - end - local serializer = implementation.serializer - local deserializer = implementation.deserializer - local old_on_activate = on_activate - function on_activate(self, staticdata, dtime) - self._ = (staticdata ~= "" and deserializer(staticdata)) or {} - if props_id then - if not self._.id then - highest_id = highest_id + 1 - self._.id = highest_id - storage:set_int("highest_id", highest_id) - end - entities_by_id[self._.id] = self - end - old_on_activate(self, staticdata, dtime) - end - function def.get_staticdata(self) - return serializer(self._) - end - end - -- TODO consider HACK for #10158 - if props.moveresult then - -- localizing variables for performance reasons - local mr = props.moveresult - local mr_collisions = mr.collisions - local mr_axes = mr.axes - local mr_old_velocity = mr.old_velocity - local mr_acc_dependent = mr.acceleration_dependent - local mr_speed_diff = mr.speed_difference - local mr_moblib = mr.moblib - local old_on_activate = on_activate - function on_activate(self, staticdata, dtime) - old_on_activate(self, staticdata, dtime) - self._last_velocity = self.object:get_velocity() - end - local old_on_step = on_step - function on_step(self, dtime, moveresult) - local obj = self.object - if engine_has_moveresult and not mr_acc_dependent and not mr_moblib then - if moveresult.collides then - if mr_axes then - local axes = {} - for _, collision in ipairs(moveresult.collisions) do - axes[collision.axis] = true - end - moveresult.axes = axes - end - if mr_old_velocity then - if not moveresult.collisions[1] then - moveresult.old_velocity = self._last_velocity - else - moveresult.old_velocity = moveresult.collisions[1].old_velocity - end - end - if mr_speed_diff then - local expected_vel = vector.add(self._last_velocity, vector.multiply(obj:get_acceleration(), dtime)) - moveresult.speed_difference = vector.length(vector.subtract(expected_vel, obj:get_velocity())) - end - end - else - moveresult = {collides = false} - if self._last_velocity then - local expected_vel = vector.add(self._last_velocity, vector.multiply(obj:get_acceleration(), dtime)) - local velocity = obj:get_velocity() - local diff = vector.subtract(expected_vel, velocity) - local speed_difference = vector.length(diff) - local collides = speed_difference >= sensitivity - moveresult.collides = collides - if collides then - if mr_speed_diff then - moveresult.speed_difference = speed_difference - end - if mr_collisions then - local collisions = {} - diff = vector.apply(diff, math.abs) - local new_velocity = self._last_velocity - for axis, component_diff in pairs(diff) do - if component_diff > sensitivity then - new_velocity[axis] = velocity[axis] - table.insert(collisions, { - axis = axis, - old_velocity = self._last_velocity, - new_velocity = new_velocity - }) - end - end - moveresult.collisions = collisions - end - if mr_axes then - local axes = {} - diff = vector.apply(diff, math.abs) - for axis, component_diff in pairs(diff) do - if component_diff > sensitivity then - axes[axis] = true - end - end - moveresult.axes = axes - end - if mr_old_velocity then - moveresult.old_velocity = self._last_velocity - end - if mr_acc_dependent then - moveresult.acceleration_dependent = vector.length(vector.subtract(self._last_velocity, velocity)) < sensitivity - end - end - end - end - old_on_step(self, dtime, moveresult) - self._last_velocity = obj:get_velocity() - end - function def._set_velocity(self, velocity) - self.object:set_velocity(velocity) - self._last_velocity = velocity - end - end - def.on_activate = on_activate - def.on_step = on_step - minetest.register_entity(name, def) -end \ No newline at end of file diff --git a/mods/moblib/mod.conf b/mods/moblib/mod.conf deleted file mode 100644 index 11d78887..00000000 --- a/mods/moblib/mod.conf +++ /dev/null @@ -1,4 +0,0 @@ -name=moblib -description=Low-level high-performance entity library -author=LMD aka appguru(eu) -depends=modlib \ No newline at end of file diff --git a/mods/modlib b/mods/modlib new file mode 160000 index 00000000..59ed71df --- /dev/null +++ b/mods/modlib @@ -0,0 +1 @@ +Subproject commit 59ed71df15e4e01e0bb096e7b1d709ea9fdb25cb diff --git a/mods/modlib/License.txt b/mods/modlib/License.txt deleted file mode 100644 index 8532a897..00000000 --- a/mods/modlib/License.txt +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2019 - 2021 Lars Mueller alias LMD or appguru(eu) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/mods/modlib/Readme.md b/mods/modlib/Readme.md deleted file mode 100644 index e8a92286..00000000 --- a/mods/modlib/Readme.md +++ /dev/null @@ -1,37 +0,0 @@ -# ![Logo](logo.svg) Modding Library (`modlib`) -Was taken from https://github.com/appgurueu/modlib, readme.md modified - -Multipurpose Minetest Modding Library - -## At a glance - -No dependencies. Licensed under the MIT License. Written by Lars Mueller aka LMD or appguru(eu). Requires Lua 5.1 / LuaJIT. - -### Acknowledgement - -* [luk3yx](https://github.com/luk3yx): Various suggestions, bug reports and fixes -* [grorp](https://github.com/grorp) (Gregor Parzefall): [Bug reports & proposed fixes for node box code](https://github.com/appgurueu/modlib/pull/8) -* [NobWow](https://github.com/NobWow/): [Another bugfix](https://github.com/appgurueu/modlib/pull/7) - -### Principles - -* Game-agnostic: Modlib aims to provide nothing game-specific; -* Minimal invasiveness: Modlib should not disrupt other mods; - even at the expense of syntactic sugar, changes to the global - environment - apart from the addition of the modlib scope - are forbidden -* Architecture: Modlib is organized hierarchically -* Performance: Modlib tries to not compromise performance for convenience; modlib loads lazily - -## Tests - -The tests are located in a different repo, [`modlib_test`](https://github.com/appgurueu/modlib_test), as they are quite heavy due to testing the PNG reader using PngSuite. Reading the tests for examples of API usage is recommended. - -## API - -(Incomplete) documentation resides in the `doc` folder; you'll have to dive into the code for everything else. - -The mod namespace is `modlib`, containing all modules which in turn contain variables & functions. - -Modules are lazily loaded by indexing the `modlib` table. Do `_ = modlib.` to avoid file load spikes at run time. - -Localizing modules (`local = modlib.`) is recommended. diff --git a/mods/modlib/b3d.lua b/mods/modlib/b3d.lua deleted file mode 100644 index 5b15a72c..00000000 --- a/mods/modlib/b3d.lua +++ /dev/null @@ -1,1147 +0,0 @@ --- Localize globals -local assert, error, math, modlib, next, ipairs, pairs, setmetatable, string_char, table - = assert, error, math, modlib, next, ipairs, pairs, setmetatable, string.char, table - -local mat4 = modlib.matrix4 - -local read_int, read_single = modlib.binary.read_int, modlib.binary.read_single - -local write_int, write_uint, write_single = modlib.binary.write_int, modlib.binary.write_uint, modlib.binary.write_single - -local fround = modlib.math.fround - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - -local metatable = {__index = _ENV} - ---+ Reads a single BB3D chunk from a stream ---+ Doing `assert(stream:read(1) == nil)` afterwards is recommended ---+ See `b3d_specification.txt` as well as https://github.com/blitz-research/blitz3d/blob/master/blitz3d/loader_b3d.cpp ---> B3D model -function read(stream) - local left = 8 - - local function byte() - left = left - 1 - return assert(stream:read(1):byte()) - end - - local function int() - return read_int(byte, 4) - end - - local function id() - return int() + 1 - end - - local function optional_id() - local id = int() - if id == -1 then - return - end - return id + 1 - end - - local function string() - local rope = {} - while true do - left = left - 1 - local char = assert(stream:read(1)) - if char == "\0" then - return table.concat(rope) - end - table.insert(rope, char) - end - end - - local function float() - return read_single(byte) - end - - local function float_array(length) - local list = {} - for index = 1, length do - list[index] = float() - end - return list - end - - local function color() - local ret = {} - ret.r = float() - ret.g = float() - ret.b = float() - ret.a = float() - return ret - end - - local function vector3() - return float_array(3) - end - - local function quaternion() - local w = float() - local x = float() - local y = float() - local z = float() - return {x, y, z, w} - end - - local function content() - if left < 0 then - error(("unexpected EOF at position %d"):format(stream:seek())) - end - return left ~= 0 - end - - local chunk - local chunks = { - TEXS = function() - local textures = {} - while content() do - local tex = {} - tex.file = string() - tex.flags = int() - tex.blend = int() - tex.pos = float_array(2) - tex.scale = float_array(2) - tex.rotation = float() - table.insert(textures, tex) - end - return textures - end, - BRUS = function() - local brushes = {} - local n_texs = int() - assert(n_texs <= 8) - while content() do - local brush = {} - brush.name = string() - brush.color = color() - brush.shininess = float() - brush.blend = int() - brush.fx = int() - brush.texture_id = {} - for index = 1, n_texs do - brush.texture_id[index] = optional_id() - end - table.insert(brushes, brush) - end - return brushes - end, - VRTS = function() - local vertices = {} - vertices.flags = int() - vertices.tex_coord_sets = int() - vertices.tex_coord_set_size = int() - assert(vertices.tex_coord_sets <= 8 and vertices.tex_coord_set_size <= 4) - local has_normal = (vertices.flags % 2 == 1) or nil - local has_color = (math.floor(vertices.flags / 2) % 2 == 1) or nil - while content() do - local vertex = {} - vertex.pos = vector3() - vertex.normal = has_normal and vector3() - vertex.color = has_color and color() - vertex.tex_coords = {} - for tex_coord_set = 1, vertices.tex_coord_sets do - local tex_coords = {} - for tex_coord = 1, vertices.tex_coord_set_size do - tex_coords[tex_coord] = float() - end - vertex.tex_coords[tex_coord_set] = tex_coords - end - table.insert(vertices, vertex) - end - return vertices - end, - TRIS = function() - local tris = {} - tris.brush_id = id() - tris.vertex_ids = {} - while content() do - local i = id() - local j = id() - local k = id() - table.insert(tris.vertex_ids, {i, j, k}) - end - return tris - end, - MESH = function() - local mesh = {} - mesh.brush_id = optional_id() - mesh.vertices = chunk{VRTS = true} - mesh.triangle_sets = {} - repeat - local tris = chunk{TRIS = true} - table.insert(mesh.triangle_sets, tris) - until not content() - return mesh - end, - BONE = function() - local bone = {} - while content() do - local vertex_id = id() - assert(not bone[vertex_id], "duplicate vertex weight") - local weight = float() - if weight > 0 then - -- Many exporters include unneeded zero weights - bone[vertex_id] = weight - end - end - return bone - end, - KEYS = function() - local flags = int() - local _flags = flags % 8 - local rotation, scale, position - if _flags >= 4 then - rotation = true - _flags = _flags - 4 - end - if _flags >= 2 then - scale = true - _flags = _flags - 2 - end - position = _flags >= 1 - local bone = { - flags = flags - } - while content() do - local frame = {} - frame.frame = int() - if position then - frame.position = vector3() - end - if scale then - frame.scale = vector3() - end - if rotation then - frame.rotation = quaternion() - end - table.insert(bone, frame) - end - return bone - end, - ANIM = function() - local ret = {} - ret.flags = int() -- flags are unused - ret.frames = int() - ret.fps = float() - return ret - end, - NODE = function() - local node = {} - node.name = string() - node.position = vector3() - node.scale = vector3() - node.keys = {} - node.rotation = quaternion() - node.children = {} - local node_type - -- See https://github.com/blitz-research/blitz3d/blob/master/blitz3d/loader_b3d.cpp#L263 - -- Order is not validated; double occurrences of mutually exclusive node def are - while content() do - local elem, type = chunk() - if type == "MESH" then - assert(not node_type) - node_type = "mesh" - node.mesh = elem - elseif type == "BONE" then - assert(not node_type) - node_type = "bone" - node.bone = elem - elseif type == "KEYS" then - modlib.table.append(node.keys, elem) - elseif type == "NODE" then - table.insert(node.children, elem) - elseif type == "ANIM" then - node.animation = elem - else - assert(not node_type) - node_type = "pivot" - end - end - -- Ensure frames are sorted ascendingly - table.sort(node.keys, function(a, b) - assert(a.frame ~= b.frame, "duplicate frame") - return a.frame < b.frame - end) - return node - end, - BB3D = function() - local version = int() - local self = { - version = { - major = math.floor(version / 100), - minor = version % 100, - }, - textures = {}, - brushes = {} - } - assert(self.version.major <= 2, "unsupported version: " .. self.version.major) - while content() do - local field, type = chunk{TEXS = true, BRUS = true, NODE = true} - if type == "TEXS" then - modlib.table.append(self.textures, field) - elseif type == "BRUS" then - modlib.table.append(self.brushes, field) - else - self.node = field - end - end - return self - end - } - - local function chunk_header() - left = left - 4 - return stream:read(4), int() - end - - function chunk(possible_chunks) - local type, new_left = chunk_header() - local parent_left - left, parent_left = new_left, left - if possible_chunks and not possible_chunks[type] then - error("expected one of " .. table.concat(modlib.table.keys(possible_chunks), ", ") .. ", found " .. type) - end - local res = assert(chunks[type])() - assert(left == 0) - left = parent_left - new_left - return res, type - end - - local self = chunk{BB3D = true} - return setmetatable(self, metatable) -end - --- Writer - -local function write_rope(self) - local rope = {} - - local written_len = 0 - local function write(str) - written_len = written_len + #str - table.insert(rope, str) - end - - local function byte(val) - write(string_char(val)) - end - - local function int(val) - write_int(byte, val, 4) - end - - local function id(val) - int(val - 1) - end - - local function optional_id(val) - int(val and (val - 1) or -1) - end - - local function string(val) - write(val) - write"\0" - end - - local function float(val) - write_single(byte, fround(val)) - end - - local function float_array(arr, len) - assert(#arr == len) - for i = 1, len do - float(arr[i]) - end - end - - local function color(val) - float(val.r) - float(val.g) - float(val.b) - float(val.a) - end - - local function vector3(val) - float_array(val, 3) - end - - local function quaternion(quat) - float(quat[4]) - float(quat[1]) - float(quat[2]) - float(quat[3]) - end - - local function chunk(name, write_func) - write(name) - - -- Insert placeholder for the 4-bit len - table.insert(rope, false) - written_len = written_len + 4 - local len_idx = #rope -- save index of placeholder - - local prev_written_len = written_len - write_func() - - -- Write the length of this chunk - local chunk_len = written_len - prev_written_len - local len_binary = {} - write_int(function(byte) - table.insert(len_binary, string_char(byte)) - end, chunk_len, 4) - rope[len_idx] = table.concat(len_binary) - end - - local function NODE(node) - chunk("NODE", function() - string(node.name) - vector3(node.position) - vector3(node.scale) - quaternion(node.rotation) - local mesh = node.mesh - if mesh then - chunk("MESH", function() - optional_id(mesh.brush_id) - local vertices = mesh.vertices - chunk("VRTS", function() - int(vertices.flags) - int(vertices.tex_coord_sets) - int(vertices.tex_coord_set_size) - for _, vertex in ipairs(vertices) do - vector3(vertex.pos) - if vertex.normal then vector3(vertex.normal) end - if vertex.color then color(vertex.color) end - for tex_coord_set = 1, vertices.tex_coord_sets do - local tex_coords = vertex.tex_coords[tex_coord_set] - for tex_coord = 1, vertices.tex_coord_set_size do - float(tex_coords[tex_coord]) - end - end - end - end) - for _, triangle_set in ipairs(mesh.triangle_sets) do - chunk("TRIS", function() - id(triangle_set.brush_id) - for _, tri in ipairs(triangle_set.vertex_ids) do - id(tri[1]) - id(tri[2]) - id(tri[3]) - end - end) - end - end) - end - if node.bone then - chunk("BONE", function() - for vertex_id, weight in pairs(node.bone) do - id(vertex_id) - float(weight) - end - end) - end - if node.keys then - local keys_by_flags = {} - for _, key in ipairs(node.keys) do - local flags = 0 - flags = flags - + (key.position and 1 or 0) - + (key.scale and 2 or 0) - + (key.rotation and 4 or 0) - keys_by_flags[flags] = keys_by_flags[flags] or {} - table.insert(keys_by_flags[flags], key) - end - for flags, keys in pairs(keys_by_flags) do - chunk("KEYS", function() - int(flags) - for _, frame in ipairs(keys) do - int(frame.frame) - if frame.position then vector3(frame.position) end - if frame.scale then vector3(frame.scale) end - if frame.rotation then quaternion(frame.rotation) end - end - end) - end - end - local anim = node.animation - if anim then - chunk("ANIM", function() - int(anim.flags) - int(anim.frames) - float(anim.fps) - end) - end - for _, child in ipairs(node.children) do - NODE(child) - end - end) - end - - chunk("BB3D", function() - int(self.version.major * 100 + self.version.minor) - if self.textures[1] then - chunk("TEXS", function() - for _, tex in ipairs(self.textures) do - string(tex.file) - int(tex.flags) - int(tex.blend) - float_array(tex.pos, 2) - float_array(tex.scale, 2) - float(tex.rotation) - end - end) - end - if self.brushes[1] then - local max_n_texs = 0 - for _, brush in ipairs(self.brushes) do - for n in pairs(brush.texture_id) do - if n > max_n_texs then - max_n_texs = n - end - end - end - chunk("BRUS", function() - int(max_n_texs) - for _, brush in ipairs(self.brushes) do - string(brush.name) - color(brush.color) - float(brush.shininess) - int(brush.blend) - int(brush.fx) - for index = 1, max_n_texs do - optional_id(brush.texture_id[index]) - end - end - end) - end - if self.node then - NODE(self.node) - end - end) - return rope -end - -function write_string(self) - return table.concat(write_rope(self)) -end - -function write(self, stream) - for _, str in ipairs(write_rope(self)) do - stream:write(str) - end -end - --- B3D to glTF converter --- See https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html ---! Highly experimental; expect bugs! -do - -- glTF constants - local array_buffer = 34962 -- "Buffer containing vertex attributes, such as vertices, texcoords or colors." - local element_array_buffer = 34963 -- "Buffer used for element indices." - local component_type = { - signed_byte = 5120, - unsigned_byte = 5121, - signed_short = 5122, - unsigned_short = 5123, - unsigned_int = 5125, - float = 5126, - } - - -- Coordinate system conversions: - -- "Blitz 3D uses a left-handed system: X+ is to the right. Y+ is up. Z+ is forward." - -- "glTF uses a right-handed coordinate system. glTF defines +Y as up, +Z as forward, and -X as right; - -- the front of a glTF asset faces +Z." - - local function translation_to_gltf(vec) - return {-vec[1], vec[2], vec[3]} -- invert the X-axis - end - - local function quaternion_to_gltf(quat) - -- TODO (!) is this correct? - return {-quat[1], quat[2], quat[3], quat[4]} -- invert the X-axis - end - - -- Convert a color from table format to glTF RGBA list format - local function color_to_gltf(col) - return {col.r, col.g, col.b, col.a} - end - - -- Basic helpers for writing to the buffer, all parameterized in terms of `write_byte` - - local function write_index(write_byte, index) - write_uint(write_byte, index - 1 --[[1-based to 0-based]], 4) - end - - local function write_float(write_byte, float) - assert(-math.huge < float and float < math.huge) - assert(-math.huge < fround(float) and fround(float) < math.huge, ("%.18g got %.18g"):format(float, fround(float))) - write_single(write_byte, fround(float)) - end - - local function write_floats(write_byte, floats, expected_len) - assert(#floats == expected_len) - for i = 1, expected_len do - write_float(write_byte, floats[i]) - end - end - - local function write_vector(write_byte, vec) - return write_floats(write_byte, vec, 3) - end - - local function write_translation(write_byte, vec) - return write_vector(write_byte, translation_to_gltf(vec)) - end - - local function write_quaternion(write_byte, quat) - -- XYZW order is already correct, but we still need to convert left-handed to right-handed - return write_floats(write_byte, quaternion_to_gltf(quat), 4) - end - - function to_gltf(self) - -- Accessor helper: Stores arrays of raw data in a buffer, produces views & accessors. - -- Everything is dumped in the same large buffer. - local buffer_rope = {} -- buffer content (table of strings) - local buffer_views = {} -- glTF buffer views - local accessors = {} -- glTF accessors - local offset = 0 -- current byte offset - local function add_accessor( - type, -- name of the composite type (e.g. SCALAR, VEC3, VEC4, MAT4, ...) - comp_type, -- name of the component type (e.g. float, unsigned_int, ...) - index, -- true / false / nil: whether this is an index (true) or vertex data (false) or neither (nil) - func -- `function(write_byte) ... return count, min, max end` to be called to write to the buffer view; - -- the count of elements written must be returned; min and max may be returned - ) - -- Always add padding to obtain a multiple of 4 - -- TODO (?) don't add padding if it isn't required - table.insert(buffer_rope, ("\0"):rep(offset % 4)) - offset = math.ceil(offset / 4) * 4 - local bytes_written = 0 - local count, min, max = func(function(byte) - table.insert(buffer_rope, string_char(byte)) - bytes_written = bytes_written + 1 - end) - assert(count) - - -- Add buffer view - table.insert(buffer_views, { - buffer = 0, -- 0-based - there only is one buffer - byteOffset = offset, - byteLength = bytes_written, - target = ((index == true) and element_array_buffer) -- index data - or ((index == false) and array_buffer) -- vertex data - or nil, -- no target hint - }) - table.insert(accessors, { - bufferView = #buffer_views - 1, -- 0-based - byteOffset = 0, -- view has correct offset - componentType = assert(component_type[comp_type]), - type = type, - count = count, - min = min, - max = max, - }) - - offset = offset + bytes_written - return #accessors - 1 -- 0-based index of the accessor - end - - local textures = {} -- glTF textures - local function add_texture(name) - -- TODO (?) add an appropriate sampler - table.insert(textures, {name = name}) - return #textures - 1 -- 0-based texture index - end - for _, tex in ipairs(self.textures) do - -- Assert that all values we don't map properly yet are defaults - -- TODO dig into Blitz3D sources to figure out the meaning of flags & blend - -- TODO (...) deal with flag value of 65536: - -- "The flags field value can conditional an additional flag value of '65536'. - -- This is used to indicate that the texture uses secondary UV values, ala the TextureCoords command." - assert(tex.flags == 1) -- TODO (?) see https://github.com/blitz-research/blitz3d/blob/master/gxruntime/gxcanvas.h#L59 - assert(tex.blend == 2) - -- Assert that the texture isn't transformed - assert(tex.rotation == 0) - assert(tex.pos[1] == 0 and tex.pos[2] == 0) - assert(tex.scale[1] == 1 and tex.scale[2] == 1) - add_texture(tex.file) - end - - -- Map brushes to materials (& textures) - local materials = {} - for i, brush in ipairs(self.brushes) do - -- Assert defaults - -- See https://github.com/blitz-research/blitz3d/blob/6beb288cb5962393684a59a4a44ac11524894939/blitz3d/brush.cpp#L164-L167: - -- 0 = default/replace, 1 = alpha, 2 = multiply, 3 = add - assert(brush.blend == 1) -- (alpha) - -- TODO (...) figure out what these "effects" are and if/how to map them to glTF - assert(brush.fx == 0) - assert(#brush.texture_id <= 1) -- TODO (...) this supports only a single texture per brush for now - local index - if brush.texture_id[1] then - index = brush.texture_id[1] -- 0-based - else - -- Implementations seem to implicitly assume textures for brushes - index = add_texture(brush.name) - end - materials[i] = { - name = brush.name, - alphaMode = "BLEND", - pbrMetallicRoughness = { - baseColorFactor = color_to_gltf(brush.color), - metallicFactor = brush.shininess, -- TODO (?) are these really equivalent? - -- Add texture if there is none - baseColorTexture = { - index = index, - -- `texCoord = 0` is the default already, no need to set it - }, - }, - } - end - - local meshes = {} - local function add_mesh(mesh, weights, add_neutral_bone) - local attributes = {} - - local vertices = mesh.vertices - attributes.POSITION = add_accessor("VEC3", "float", false, function(write_byte) - local inf = math.huge - local min_pos, max_pos = {inf, inf, inf}, {-inf, -inf, -inf} - for _, vertex in ipairs(mesh.vertices) do - local pos = translation_to_gltf(vertex.pos) - write_vector(write_byte, pos) - min_pos = modlib.vector.combine(min_pos, pos, math.min) - max_pos = modlib.vector.combine(max_pos, pos, math.max) - end - return #mesh.vertices, min_pos, max_pos -- vertex accessors MUST provide min & max - end) - - local has_normals = vertices.flags % 2 == 1 -- lowest bit set? - if has_normals then - attributes.NORMAL = add_accessor("VEC3", "float", false, function(write_byte) - for _, vertex in ipairs(mesh.vertices) do - -- Some B3D models don't seem to have their normals normalized. - -- TODO (?) raise a warning when handling this gracefully - write_translation(write_byte, modlib.vector.normalize(vertex.normal)) - end - return #mesh.vertices - end) - end - - local has_colors = vertices.flags % 4 >= 2 -- second lowest bit set? - if has_colors then - attributes.COLOR_0 = add_accessor("VEC4", "float", false, function(write_byte) - for _, vertex in ipairs(mesh.vertices) do - write_floats(write_byte, color_to_gltf(vertex.color), 4) - end - return #mesh.vertices - end) - end - - if vertices.tex_coord_sets >= 1 then - assert(vertices.tex_coord_set_size == 2) - for tex_coord_set = 1, vertices.tex_coord_sets do - local tcs_id = tex_coord_set - 1 -- 0-based - attributes[("TEXCOORD_%d"):format(tcs_id)] = add_accessor("VEC2", "float", false, function(write_byte) - for _, vertex in ipairs(mesh.vertices) do - write_floats(write_byte, vertex.tex_coords[tex_coord_set], 2) - end - return #mesh.vertices - end) - end - end - - if next(weights) ~= nil then - -- Count (& pack into list) joints influencing vertices, normalize weights - local max_count = 0 - local joint_ids = {} - local normalized_weights = {} - -- Handle (supposedly) animated/dynamic vertices (can still be static by having zero weights) - for vertex_id, joint_weights in pairs(weights) do - local total_weight = 0 - local count = 0 - for _, weight in pairs(joint_weights) do - total_weight = total_weight + weight - count = count + 1 - end - if total_weight > 0 then -- animated? - joint_ids[vertex_id] = {} - normalized_weights[vertex_id] = {} - for joint, weight in pairs(joint_weights) do - table.insert(joint_ids[vertex_id], joint) - table.insert(normalized_weights[vertex_id], weight / total_weight) - end - max_count = math.max(max_count, count) - end - end - -- Now search for static vertices - for vertex_id in ipairs(mesh.vertices) do - if not joint_ids[vertex_id] then - -- Vertex isn't influenced by any bones => Add a dummy neutral bone to influence this vertex - -- See https://github.com/KhronosGroup/glTF/issues/2269 - -- and https://github.com/KhronosGroup/glTF-Blender-IO/pull/1552/ - joint_ids[vertex_id] = {add_neutral_bone()} - normalized_weights[vertex_id] = {1} - max_count = math.max(max_count, 1) -- it is (theoretically) possible that all vertices are static - end - end - assert(max_count > 0) -- TODO (?) warning for max_count > 4 - for set_start = 1, max_count, 4 do -- Iterate sets of 4 bones - local set_id = math.floor(set_start / 4) -- 0-based => floor rather than ceil - -- Write the joint IDs - attributes[("JOINTS_%d"):format(set_id)] = add_accessor("VEC4", "unsigned_short", false, function(write_byte) - for vertex_id in ipairs(mesh.vertices) do - for i = set_start, set_start + 3 do - local vrt_joint_ids, vrt_norm_weights = assert(joint_ids[vertex_id]), assert(normalized_weights[vertex_id]) - assert(#vrt_joint_ids == #vrt_norm_weights) - local id = vrt_joint_ids[i] or 0 - local weight = vrt_norm_weights[i] or 0 - if weight == 0 then - id = 0 -- required by the glTF spec - end - write_uint(write_byte, id, 2) - end - end - return #mesh.vertices - end) - -- Write the corresponding weights - attributes[("WEIGHTS_%d"):format(set_id)] = add_accessor("VEC4", "float", false, function(write_byte) - for vertex_id in ipairs(mesh.vertices) do - for i = set_start, set_start + 3 do - local weight = (normalized_weights[vertex_id] or {})[i] or 0 - write_float(write_byte, weight) - end - end - return #mesh.vertices - end) - end - end - - -- Write the indices per triangle set - local primitives = {} - for i, triangle_set in ipairs(mesh.triangle_sets) do - local index_accessor = add_accessor("SCALAR", "unsigned_int", true, function(write_byte) - for _, tri in ipairs(triangle_set.vertex_ids) do - -- Flip winding order due to the coordinate system transformation - -- TODO (!) is this correct? - for j = 3, 1, -1 do - write_index(write_byte, tri[j]) - end - end - return 3 * #triangle_set.vertex_ids - end) - -- Each triangle set is equivalent to one glTF "primitive" - local brush_id = triangle_set.brush_id or mesh.brush_id - if brush_id == 0 then -- default brush - brush_id = nil -- TODO (?) add default material if there are UVs - else - brush_id = brush_id - 1 -- 0-based - end - primitives[i] = { - attributes = attributes, - indices = index_accessor, - material = brush_id, - -- `mode = 4` (triangles) is the default already, no need to set it - } - end - - table.insert(meshes, {primitives = primitives}) - return #meshes - 1 -- 0-based - end - - -- glTF lists - local nodes = {} - local skins = {} - local samplers = {} - local channels = {} - local function add_node( - node, -- b3d node to add - bind_mat, -- bind matrix of the parent bone (may be `nil` if none) - fps, -- fps of the parent bone (may be `nil` if none) - anim -- shared animation of the parent mesh - ) - table.insert(nodes, false) -- HACK first insert a placeholder to get a fixed ID - local node_id = #nodes - 1 -- 0-indexed <=> before `table.insert`! - - -- Animation (speed)? - fps = node.animation and node.animation.fps or fps - - -- Keyframes? - if node.keys then - -- Convert from a list of keyframes of three overrides to three lists of channels - local targets = { - translation = {output_type = "VEC3", b3d_field = "position", write_value = write_translation}, - scale = {output_type = "VEC3", b3d_field = "scale", write_value = write_vector}, - rotation = {output_type = "VEC4", b3d_field = "rotation", write_value = write_quaternion} - } - for _, keyframe in ipairs(node.keys) do - local frame = keyframe.frame - for _, target in pairs(targets) do - local value = keyframe[target.b3d_field] - if value then - table.insert(target, {frame = frame, value = value}) - end - end - end - for target, keyframes in pairs(targets) do - if #keyframes > 0 then - -- Write input (timestamps) - local input = add_accessor("SCALAR", "float", nil, function(write_byte) - local min, max = math.huge, -math.huge - for _, keyframe in ipairs(keyframes) do - local sec = keyframe.frame / (fps or 60) -- convert frames to seconds; default FPS is 60 - write_float(write_byte, sec) - min, max = math.min(min, sec), math.max(max, sec) - end - return #keyframes, {min}, {max} -- min and max are mandatory - end) - - -- Write output (overrides) - local output = add_accessor(keyframes.output_type, "float", nil, function(write_byte) - for _, keyframe in ipairs(keyframes) do - keyframes.write_value(write_byte, keyframe.value) - end - return #keyframes - end) - - table.insert(samplers, { - input = input, - output = output, - -- interpolation default is already linear, matching b3d - }) - - table.insert(channels, { - sampler = #samplers - 1, -- 0-based - target = { - node = node_id, - path = target, - } - }) - end - end - end - - if node.mesh then - -- Initialize skeletal animation - assert(not anim) - anim = { - weights = {}, - joints = {}, - inv_bind_mats = {}, - } - end - - if node.bone then - local joint_id = #anim.joints - table.insert(anim.joints, node_id) - - -- "To compose the local transformation matrix, TRS properties MUST be converted to matrices and postmultiplied in - -- the T * R * S order; first the scale is applied to the vertices, then the rotation, and then the translation." - local translation = translation_to_gltf(node.position) - local rotation = modlib.quaternion.normalize(quaternion_to_gltf(node.rotation)) - local scale = node.scale - local loc_trans_mat = mat4.scale(scale) - :compose(mat4.rotation(rotation)) - :compose(mat4.translation(translation)) - - -- Compute a proper inverse bind matrix as the inverse of the product of the transformation matrices - -- along the path from the root (the mesh) to the current node (the bone). - -- See e.g. https://stackoverflow.com/questions/17127994/opengl-bone-animation-why-do-i-need-inverse-of-bind-pose-when-working-with-gp - -- https://computergraphics.stackexchange.com/questions/7603/confusion-about-how-inverse-bind-pose-is-actually-calculated-and-used - bind_mat = bind_mat and bind_mat:multiply(loc_trans_mat) or loc_trans_mat - table.insert(anim.inv_bind_mats, bind_mat:inverse()) - - -- Insert into reverse lookup `anim.weights[vertex_id][joint_id] = weight` - -- such that writing the mesh can then write the weights per vertex - for vertex_id, weight in pairs(node.bone) do - if weight > 0 then - anim.weights[vertex_id] = anim.weights[vertex_id] or {} - anim.weights[vertex_id][joint_id] = weight - end - end - end - - local children = {} - for _, child in ipairs(node.children) do - table.insert(children, add_node(child, bind_mat, fps, anim)) - end - local mesh, skin_id, neutral_node_id - if node.mesh then - local neutral_joint_id - -- Lazily adds a placeholder for the neutral joint, returns joint ID - local function add_neutral_joint() - if neutral_joint_id then - return neutral_joint_id - end - neutral_node_id = #nodes -- 0-based - table.insert(nodes, { - name = "neutral_bone", - -- We need to flip the hierarchy: The neutral bone must be a parent of the mesh root; - -- if it were a sibling, there would be no common skeleton root (accepted by Blender but not by glTF validator); - -- if it were a child, transformations of the mesh root would affect it and it wouldn't be a neutral bone anymore. - children = {node_id}, - -- translation, scale, rotation all default to identity - }) - neutral_joint_id = #anim.joints -- 0-based - table.insert(anim.joints, neutral_node_id) - return neutral_joint_id -- 0-based - end - mesh = add_mesh(node.mesh, anim.weights, add_neutral_joint) - if anim.joints and anim.joints[1] then - if neutral_joint_id then - -- Duplicate the inverse bind matrix of the parent (which the neutral bone will be a child of) - table.insert(anim.inv_bind_mats, bind_mat or mat4.identity()) - end - table.insert(skins, { - inverseBindMatrices = add_accessor("MAT4", "float", nil, function(write_byte) - for _, inv_bind_mat in ipairs(anim.inv_bind_mats) do - assert(#inv_bind_mat == 4) - -- glTF uses column-major order (we use row-major order) - for i = 1, 4 do - for j = 1, 4 do - write_float(write_byte, inv_bind_mat[j][i]) - end - end - end - return #anim.inv_bind_mats - end), - joints = anim.joints, - skeleton = neutral_node_id, -- make the neutral bone the skeleton root - }) - skin_id = #skins - 1 -- 0-based - end - end - -- Now replace the placeholder - nodes[node_id + 1 --[[0-based to 1-based]]] = { - name = node.name, - mesh = mesh, - skin = skin_id, - children = children[1] and children, -- glTF does not allow empty lists - translation = translation_to_gltf(node.position), - scale = node.scale, - rotation = quaternion_to_gltf(node.rotation), - } - -- If a neutral bone exists, return the neutral bone (which has the node as a child) instead of the node - return neutral_node_id or node_id -- 0-based - end - - local scene, scenes - if self.node then - scene, scenes = 0, {{nodes = {add_node(self.node)}}} - end - - local buffer_string = table.concat(buffer_rope) - return { - asset = { - generator = "modlib b3d:to_gltf", - version = "2.0" - }, - -- Textures - textures = textures[1] and textures, -- glTF does not allow empty lists - materials = materials[1] and materials, - -- Accessors, buffer views & buffers - accessors = accessors, - bufferViews = buffer_views, - buffers = { - { - byteLength = #buffer_string, - uri = "data:application/octet-stream;base64," - .. modlib.base64.encode(buffer_string) -- Note: Blender requires base64 padding - }, - }, - -- Meshes & nodes - meshes = meshes, - nodes = nodes, - -- A scene is not strictly needed but is useful for getting rid of validator warnings & having a proper root defined - scene = scene, - scenes = scenes, - -- Animation - skins = skins, - -- B3D only contains (up to) a single animation - animations = channels[1] and { - { - channels = channels, - samplers = samplers, - }, - }, - } - end -end - -function write_gltf(self, file) - modlib.json:write_file(self:to_gltf(), file) -end - -local binary_search_frame = modlib.table.binary_search_comparator(function(a, b) - return modlib.table.default_comparator(a, b.frame) -end) - ---> list of { bone_name = string, parent_bone_name = string, position = vector, rotation = quaternion, scale = vector } -function get_animated_bone_properties(self, keyframe, interpolate) - local function get_frame_values(keys) - local values = keys[keyframe] - if values and values.frame == keyframe then - return { - position = values.position, - rotation = values.rotation, - scale = values.scale - } - end - local index = binary_search_frame(keys, keyframe) - if index > 0 then - return keys[index] - end - index = -index - assert(index > 1 and index <= #keys) - local a, b = keys[index - 1], keys[index] - if not interpolate then - return a - end - local ratio = (keyframe - a.frame) / (b.frame - a.frame) - return { - position = (a.position and b.position and modlib.vector.interpolate(a.position, b.position, ratio)) or a.position or b.position, - rotation = (a.rotation and b.rotation and modlib.quaternion.slerp(a.rotation, b.rotation, ratio)) or a.rotation or b.rotation, - scale = (a.scale and b.scale and modlib.vector.interpolate(a.scale, b.scale, ratio)) or a.scale or b.scale, - } - end - local bone_properties = {} - local function get_props(node, parent_bone_name) - local properties = {parent_bone_name = parent_bone_name} - - if keyframe > 0 and node.keys and next(node.keys) ~= nil then - modlib.table.add_all(properties, get_frame_values(node.keys)) - end - - if not properties.position then -- animation not present, fall back to node position - properties.position = modlib.table.copy(node.position) - end - - if properties.rotation then -- animation is relative to node rotation - properties.rotation = modlib.quaternion.compose(node.rotation, properties.rotation) - else - properties.rotation = modlib.table.copy(node.rotation) - end - - if not properties.scale then -- animation not present, fall back to node scale - properties.scale = modlib.table.copy(node.scale) - end - - if node.bone then - properties.bone_name = node.name - table.insert(bone_properties, properties) - end - for _, child in pairs(node.children or {}) do - get_props(child, properties.bone_name) - end - end - get_props(self.node) - return bone_properties -end - --- Export environment -return _ENV diff --git a/mods/modlib/base64.lua b/mods/modlib/base64.lua deleted file mode 100644 index b1f2a2a8..00000000 --- a/mods/modlib/base64.lua +++ /dev/null @@ -1,132 +0,0 @@ -local assert, floor, char, insert, concat = assert, math.floor, string.char, table.insert, table.concat - -local base64 = {} - -local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" - ---! This is currently 5 - 10x slower than a C(++) implementation like Minetest's `minetest.encode_base64` -function base64.encode( - str, -- byte string to encode - padding -- whether to add padding, defaults to `true` -) - local res = {} - for i = 1, #str - 2, 3 do - -- Convert 3 bytes to 4 sextets - local b1, b2, b3 = str:byte(i, i + 2) - insert(res, char( - alphabet:byte(floor(b1 / 4) + 1), -- high 6 bits of first byte - alphabet:byte(16 * (b1 % 4) + floor(b2 / 16) + 1), -- low 2 bits of first byte & high 4 bits of second byte - alphabet:byte(4 * (b2 % 16) + floor(b3 / 64) + 1), -- low 4 bits of second byte & high 2 bits of third byte - alphabet:byte((b3 % 64) + 1) -- low 6 bits of third byte - )) - end - -- Handle remaining 1 or 2 bytes: - -- Treat "missing" bytes to a multiple of 3 as "0" bytes, add appropriate padding. - local bytes_left = #str % 3 - if bytes_left == 1 then - local b = str:byte(#str) -- b2 and b3 are missing ("= 0") - insert(res, char( - alphabet:byte(floor(b / 4) + 1), - alphabet:byte(16 * (b % 4) + 1) - )) - -- Last two sextets depend only on missing bytes => padding - if padding ~= false then - insert(res, "==") - end - elseif bytes_left == 2 then - local b1, b2 = str:byte(#str - 1, #str) -- b3 is missing ("= 0") - insert(res, char( - alphabet:byte(floor(b1 / 4) + 1), - alphabet:byte(16 * (b1 % 4) + floor(b2 / 16) + 1), - alphabet:byte(4 * (b2 % 16) + 1) - )) - -- Last sextet depends only on missing byte => padding - if padding ~= false then - insert(res, "=") - end - end - - return concat(res) -end - --- Build reverse lookup table -local values = {} -for i = 1, #alphabet do - values[alphabet:byte(i)] = i - 1 -end - -local function decode_sextets_2(b1, b2) - local v1, v2 = values[b1], values[b2] - assert(v1 and v2) - assert(v2 % 16 == 0) -- 4 low bits from second sextet must be 0 - return char(4 * v1 + floor(v2 / 16)) -- first sextet + 2 high bits from second sextet -end - -local function decode_sextets_3(b1, b2, b3) - local v1, v2, v3 = values[b1], values[b2], values[b3] - assert(v1 and v2 and v3) - assert(v3 % 4 == 0) -- 2 low bits from third sextet must be 0 - return char( - 4 * v1 + floor(v2 / 16), -- first sextet + 2 high bits from second sextet - 16 * (v2 % 16) + floor(v3 / 4) -- 4 low bits from second sextet + 4 high bits from third sextet - ) -end - -local function decode_sextets_4(b1, b2, b3, b4) - local v1, v2, v3, v4 = values[b1], values[b2], values[b3], values[b4] - assert(v1 and v2 and v3 and v4) - return char( - 4 * v1 + floor(v2 / 16), -- first sextet + 2 high bits from second sextet - 16 * (v2 % 16) + floor(v3 / 4), -- 4 low bits from second sextet + 4 high bits from third sextet - 64 * (v3 % 4) + v4 -- 2 low bits from third sextet + fourth sextet - ) -end - ---! This is also about 10x slower than a C(++) implementation like Minetest's `minetest.decode_base64` -function base64.decode( - -- base64-encoded string to decode - str, - -- Whether to expect padding: - -- * `nil` (default) - may (or may not) be padded, - -- * `false` - must not be padded, - -- * `true` - must be padded - padding -) - -- Handle the empty string - the below code expects a nonempty string - if str == "" then return "" end - - local res = {} - -- Note: the last (up to) 4 sextets are deliberately excluded, since they may contain padding - for i = 1, #str - 4, 4 do - -- Convert 4 sextets to 3 bytes - insert(res, decode_sextets_4(str:byte(i, i + 3))) - end - local sextets_left = #str % 4 - if sextets_left == 0 then -- possibly padded - -- Convert 4 sextets to 3 bytes, taking padding into account - local b3, b4 = str:byte(#str - 1, #str) - if b3 == ("="):byte() then - assert(b4 == ("="):byte()) - assert(padding ~= false, "got padding") - insert(res, decode_sextets_2(str:byte(#str - 3, #str - 2))) - elseif b4 == ("="):byte() then - assert(padding ~= false, "got padding") - insert(res, decode_sextets_3(str:byte(#str - 3, #str - 1))) - else -- no padding necessary - assert(#str >= 4) - assert(#({str:byte(#str - 3, #str)}) == 4) - insert(res, decode_sextets_4(str:byte(#str - 3, #str))) - end - else -- no padding and length not divisible by 4 - assert(padding ~= true, "missing/invalid padding") - assert(sextets_left ~= 1) - if sextets_left == 2 then - insert(res, decode_sextets_2(str:byte(#str - 1, #str))) - elseif sextets_left == 3 then - insert(res, decode_sextets_3(str:byte(#str - 2, #str))) - end - end - return concat(res) -end - -return base64 \ No newline at end of file diff --git a/mods/modlib/binary.lua b/mods/modlib/binary.lua deleted file mode 100644 index c4f7d81d..00000000 --- a/mods/modlib/binary.lua +++ /dev/null @@ -1,236 +0,0 @@ --- Localize globals -local assert, math_huge, math_frexp, math_floor - = assert, math.huge, math.frexp, math.floor - -local positive_nan, negative_nan = modlib.math.positive_nan, modlib.math.negative_nan - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - --- All little endian - ---+ Reads an IEEE 754 single-precision floating point number (f32) -function read_single(read_byte) - -- First read the mantissa - local mantissa = read_byte() / 0x100 - mantissa = (mantissa + read_byte()) / 0x100 - - -- Second and first byte in big endian: last bit of exponent + 7 bits of mantissa, sign bit + 7 bits of exponent - local exponent_byte = read_byte() - local sign_byte = read_byte() - local sign = 1 - if sign_byte >= 0x80 then - sign = -1 - sign_byte = sign_byte - 0x80 - end - local exponent = sign_byte * 2 - if exponent_byte >= 0x80 then - exponent = exponent + 1 - exponent_byte = exponent_byte - 0x80 - end - mantissa = (mantissa + exponent_byte) / 0x80 - if exponent == 0xFF then - if mantissa == 0 then - return sign * math_huge - end - -- Differentiating quiet and signalling nan is not possible in Lua, hence we don't have to do it - return sign == 1 and positive_nan or negative_nan - end - assert(mantissa < 1) - if exponent == 0 then - -- subnormal value - return sign * 2^-126 * mantissa - end - return sign * 2 ^ (exponent - 127) * (1 + mantissa) -end - ---+ Reads an IEEE 754 double-precision floating point number (f64) -function read_double(read_byte) - -- First read the mantissa - local mantissa = 0 - for _ = 1, 6 do - mantissa = (mantissa + read_byte()) / 0x100 - end - -- Second and first byte in big endian: last 4 bits of exponent + 4 bits of mantissa; sign bit + 7 bits of exponent - local exponent_byte = read_byte() - local sign_byte = read_byte() - local sign = 1 - if sign_byte >= 0x80 then - sign = -1 - sign_byte = sign_byte - 0x80 - end - local exponent = sign_byte * 0x10 - local mantissa_bits = exponent_byte % 0x10 - exponent = exponent + (exponent_byte - mantissa_bits) / 0x10 - mantissa = (mantissa + mantissa_bits) / 0x10 - if exponent == 0x7FF then - if mantissa == 0 then - return sign * math_huge - end - -- Differentiating quiet and signalling nan is not possible in Lua, hence we don't have to do it - return sign == 1 and positive_nan or negative_nan - end - assert(mantissa < 1) - if exponent == 0 then - -- subnormal value - return sign * 2^-1022 * mantissa - end - return sign * 2 ^ (exponent - 1023) * (1 + mantissa) -end - ---+ Reads doubles (f64) or floats (f32) ---: double reads an f64 if true, f32 otherwise -function read_float(read_byte, double) - return (double and read_double or read_single)(read_byte) -end - -function read_uint(read_byte, bytes) - local factor = 1 - local uint = 0 - for _ = 1, bytes do - uint = uint + read_byte() * factor - factor = factor * 0x100 - end - return uint -end - -function read_int(read_byte, bytes) - local uint = read_uint(read_byte, bytes) - local max = 0x100 ^ bytes - if uint >= max / 2 then - return uint - max - end - return uint -end - -function write_uint(write_byte, uint, bytes) - for _ = 1, bytes do - write_byte(uint % 0x100) - uint = math_floor(uint / 0x100) - end - assert(uint == 0) -end - -function write_int(write_byte, int, bytes) - local max = 0x100 ^ bytes - if int < 0 then - assert(-int <= max / 2) - int = max + int - else - assert(int < max / 2) - end - return write_uint(write_byte, int, bytes) -end - -function write_single(write_byte, number) - if number ~= number then -- nan: all ones - for _ = 1, 4 do write_byte(0xFF) end - return - end - - local sign_byte, exponent_byte, mantissa_byte_1, mantissa_byte_2 - - local sign_bit = 0 - if number < 0 then - number = -number - sign_bit = 0x80 - end - - if number == math_huge then -- inf: exponent = all 1, mantissa = all 0 - sign_byte, exponent_byte, mantissa_byte_1, mantissa_byte_2 = sign_bit + 0x7F, 0x80, 0, 0 - else -- real number - local mantissa, exponent = math_frexp(number) - if exponent <= -126 or number == 0 then -- must write a subnormal number - mantissa = mantissa * 2 ^ (exponent + 126) - exponent = 0 - else -- normal numbers are stored as 1. - mantissa = mantissa * 2 - 1 - exponent = exponent - 1 + 127 -- mantissa << 1 <=> exponent-- - assert(exponent < 0xFF) - end - - local exp_lowest_bit = exponent % 2 - - sign_byte = sign_bit + (exponent - exp_lowest_bit) / 2 - - mantissa = mantissa * 0x80 - exponent_byte = exp_lowest_bit * 0x80 + math_floor(mantissa) - mantissa = mantissa % 1 - - mantissa = mantissa * 0x100 - mantissa_byte_1 = math_floor(mantissa) - mantissa = mantissa % 1 - - mantissa = mantissa * 0x100 - mantissa_byte_2 = math_floor(mantissa) - mantissa = mantissa % 1 - - assert(mantissa == 0) -- no truncation allowed: round numbers properly using modlib.math.fround - end - - write_byte(mantissa_byte_2) - write_byte(mantissa_byte_1) - write_byte(exponent_byte) - write_byte(sign_byte) -end - -function write_double(write_byte, number) - if number ~= number then -- nan: all ones - for _ = 1, 8 do write_byte(0xFF) end - return - end - - local sign_byte, exponent_byte, mantissa_bytes - - local sign_bit = 0 - if number < 0 then - number = -number - sign_bit = 0x80 - end - - if number == math_huge then -- inf: exponent = all 1, mantissa = all 0 - sign_byte, exponent_byte, mantissa_bytes = sign_bit + 0x7F, 0xF0, {0, 0, 0, 0, 0, 0} - else -- real number - local mantissa, exponent = math_frexp(number) - if exponent <= -1022 or number == 0 then -- must write a subnormal number - mantissa = mantissa * 2 ^ (exponent + 1022) - exponent = 0 - else -- normal numbers are stored as 1. - mantissa = mantissa * 2 - 1 - exponent = exponent - 1 + 1023 -- mantissa << 1 <=> exponent-- - assert(exponent < 0x7FF) - end - - local exp_low_nibble = exponent % 0x10 - - sign_byte = sign_bit + (exponent - exp_low_nibble) / 0x10 - - mantissa = mantissa * 0x10 - exponent_byte = exp_low_nibble * 0x10 + math_floor(mantissa) - mantissa = mantissa % 1 - - mantissa_bytes = {} - for i = 1, 6 do - mantissa = mantissa * 0x100 - mantissa_bytes[i] = math_floor(mantissa) - mantissa = mantissa % 1 - end - assert(mantissa == 0) - end - - for i = 6, 1, -1 do - write_byte(mantissa_bytes[i]) - end - write_byte(exponent_byte) - write_byte(sign_byte) -end - ---: on_write function(double) ---: double true - f64, false - f32 -function write_float(write_byte, number, double) - (double and write_double or write_single)(write_byte, number) -end - --- Export environment -return _ENV diff --git a/mods/modlib/bluon.lua b/mods/modlib/bluon.lua deleted file mode 100644 index a548def3..00000000 --- a/mods/modlib/bluon.lua +++ /dev/null @@ -1,333 +0,0 @@ --- Localize globals -local assert, error, ipairs, math_floor, math_abs, math_huge, modlib, next, pairs, setmetatable, string, table_insert, type, unpack - = assert, error, ipairs, math.floor, math.abs, math.huge, modlib, next, pairs, setmetatable, string, table.insert, type, unpack - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - -local fround = modlib.math.fround -local write_single, write_double = modlib.binary.write_single, modlib.binary.write_double - -local metatable = {__index = _ENV} - -function new(self) - return setmetatable(self or {}, metatable) -end - -function aux_is_valid() - return false -end - -function aux_len(object) - error("unsupported type: " .. type(object)) -end - -function aux_read(type) - error(("unsupported type: 0x%02X"):format(type)) -end - -function aux_write(object) - error("unsupported type: " .. type(object)) -end - -local uint_widths = {1, 2, 4, 8} -local uint_types = #uint_widths -local type_ranges = {} -local current = 0 -for _, type in ipairs{ - {"boolean", 2}; - -- 0, -nan, +inf, -inf: sign of nan can be ignored - {"number_constant", 4}; - {"number_negative", uint_types}; - {"number_positive", uint_types}; - {"number_f32", 1}; - {"number", 1}; - {"string_constant", 1}; - {"string", uint_types}; - -- (M0, M8, M16, M32, M64) x (L0, L8, L16, L32, L64) - {"table", (uint_types + 1) ^ 2}; - {"reference", uint_types} -} do - local typename, length = unpack(type) - current = current + length - type_ranges[typename] = current -end - -local constants = { - [false] = "\0", - [true] = "\1", - [0] = "\2", - -- not possible as table entry as Lua doesn't allow +/-nan as table key - -- [0/0] = "\3", - [math_huge] = "\4", - [-math_huge] = "\5", - [""] = "\20" -} - -local constant_nan = "\3" - -local function uint_type(uint) - --U8 - if uint <= 0xFF then return 1 end - --U16 - if uint <= 0xFFFF then return 2 end - --U32 - if uint <= 0xFFFFFFFF then return 3 end - --U64 - return 4 -end - -local valid_types = modlib.table.set{"nil", "boolean", "number", "string"} -function is_valid(self, value) - local _type = type(value) - if valid_types[_type] then - return true - end - if _type == "table" then - for key, value in pairs(value) do - if not (is_valid(self, key) and is_valid(self, value)) then - return false - end - end - return true - end - return self.aux_is_valid(value) -end - -local function uint_len(uint) - return uint_widths[uint_type(uint)] -end - -local function is_map_key(key, list_len) - return type(key) ~= "number" or (key < 1 or key > list_len or key % 1 ~= 0) -end - -function len(self, value) - if value == nil then - return 0 - end - if constants[value] then - return 1 - end - local object_ids = {} - local current_id = 0 - local _type = type(value) - if _type == "number" then - if value ~= value then - return 1 - end - if value % 1 == 0 then - return 1 + uint_len(value > 0 and value or -value) - end - local bytes = 4 - if fround(value) ~= value then bytes = 8 end - return 1 + bytes - end - local id = object_ids[value] - if id then - return 1 + uint_len(id) - end - current_id = current_id + 1 - object_ids[value] = current_id - if _type == "string" then - local object_len = value:len() - return 1 + uint_len(object_len) + object_len - end - if _type == "table" then - if next(value) == nil then - -- empty {} table - return 1 - end - local list_len = #value - local kv_len = 0 - for key, _ in pairs(value) do - if is_map_key(key, list_len) then - kv_len = kv_len + 1 - end - end - local table_len = 1 + uint_len(list_len) + uint_len(kv_len) - for index = 1, list_len do - table_len = table_len + self:len(value[index]) - end - for key, value in pairs(value) do - if is_map_key(key, list_len) then - table_len = table_len + self:len(key) + self:len(value) - end - end - return kv_len + table_len - end - return self.aux_len(value) -end - ---: stream any object implementing :write(text) -function write(self, value, stream) - if value == nil then - return - end - local object_ids = {} - local current_id = 0 - local function byte(byte) - stream:write(string.char(byte)) - end - local write_uint = modlib.binary.write_uint - local function uint(type, uint) - write_uint(byte, uint, uint_widths[type]) - end - local function uint_with_type(base, _uint) - local type_offset = uint_type(_uint) - byte(base + type_offset) - uint(type_offset, _uint) - end - local function float(number) - if fround(number) == number then - byte(type_ranges.number_f32) - write_single(byte, number) - else - byte(type_ranges.number) - write_double(byte, number) - end - end - local aux_write = self.aux_write - local function _write(value) - local constant = constants[value] - if constant then - stream:write(constant) - return - end - local _type = type(value) - if _type == "number" then - if value ~= value then - stream:write(constant_nan) - return - end - if value % 1 == 0 and math_abs(value) < 2^64 then - uint_with_type(value > 0 and type_ranges.number_constant or type_ranges.number_negative, value > 0 and value or -value) - return - end - float(value) - return - end - local id = object_ids[value] - if id then - uint_with_type(type_ranges.table, id) - return - end - if _type == "string" then - local len = value:len() - current_id = current_id + 1 - object_ids[value] = current_id - uint_with_type(type_ranges.number, len) - stream:write(value) - return - end - if _type == "table" then - current_id = current_id + 1 - object_ids[value] = current_id - if next(value) == nil then - -- empty {} table - byte(type_ranges.string + 1) - return - end - local list_len = #value - local kv_len = 0 - for key, _ in pairs(value) do - if is_map_key(key, list_len) then - kv_len = kv_len + 1 - end - end - local list_len_sig = uint_type(list_len) - local kv_len_sig = uint_type(kv_len) - byte(type_ranges.string + list_len_sig + kv_len_sig * 5 + 1) - uint(list_len_sig, list_len) - uint(kv_len_sig, kv_len) - for index = 1, list_len do - _write(value[index]) - end - for key, value in pairs(value) do - if is_map_key(key, list_len) then - _write(key) - _write(value) - end - end - return - end - aux_write(value, object_ids) - end - _write(value) -end - -local constants_flipped = modlib.table.flip(constants) -constants_flipped[constant_nan] = 0/0 - --- See https://www.lua.org/manual/5.1/manual.html#2.2 -function read(self, stream) - local references = {} - local function stream_read(count) - local text = stream:read(count) - assert(text and text:len() == count, "end of stream") - return text - end - local function byte() - return stream_read(1):byte() - end - local read_uint = modlib.binary.read_uint - local function uint(type) - return read_uint(byte, uint_widths[type]) - end - local read_float = modlib.binary.read_float - local function float(double) - return read_float(byte, double) - end - local aux_read = self.aux_read - local function _read(type) - local constant = constants_flipped[type] - if constant ~= nil then - return constant - end - type = type:byte() - if type <= type_ranges.number then - if type <= type_ranges.number_negative then - return uint(type - type_ranges.number_constant) - end - if type <= type_ranges.number_positive then - return -uint(type - type_ranges.number_negative) - end - return float(type == type_ranges.number) - end - if type <= type_ranges.string then - local string = stream_read(uint(type - type_ranges.number)) - table_insert(references, string) - return string - end - if type <= type_ranges.table then - type = type - type_ranges.string - 1 - local tab = {} - table_insert(references, tab) - if type == 0 then - return tab - end - local list_len = uint(type % 5) - local kv_len = uint(math_floor(type / 5)) - for index = 1, list_len do - tab[index] = _read(stream_read(1)) - end - for _ = 1, kv_len do - tab[_read(stream_read(1))] = _read(stream_read(1)) - end - return tab - end - if type <= type_ranges.reference then - return references[uint(type - type_ranges.table)] - end - return aux_read(type, stream, references) - end - local type = stream:read(1) - if type == nil then - return - end - return _read(type) -end - --- Export environment -return _ENV diff --git a/mods/modlib/build/html_entities.lua b/mods/modlib/build/html_entities.lua deleted file mode 100644 index 60083f14..00000000 --- a/mods/modlib/build/html_entities.lua +++ /dev/null @@ -1,19 +0,0 @@ --- Generate lookup table for HTML entities out of https://html.spec.whatwg.org/entities.json --- Requires https://github.com/brunoos/luasec to fetch the JSON -local https = require 'ssl.https' -local res, code = https.request"https://html.spec.whatwg.org/entities.json" -assert(code == 200) -local entity_map = {} -for entity, chars in pairs(assert(modlib.json:read_string(res))) do - entity_map[entity:sub(2, #entity - 1)] = table.concat(modlib.table.map(chars.codepoints, modlib.utf8.char)) -end -local entries = {} -for entity, chars in pairs(entity_map) do - table.insert(entries, ("[%q] = %q"):format(entity, chars)) -end -local serialized = [[-- HTML entity lookup table generated by build/html_entities.lua. Do not edit. -return {]] .. table.concat(entries, ", ") .. "}" -local loaded = assert(loadstring(serialized)) -setfenv(loaded, {}) -assert(modlib.table.equals(entity_map, loaded())) -modlib.file.write(modlib.mod.get_resource("modlib", "web", "html", "entities.lua"), serialized) \ No newline at end of file diff --git a/mods/modlib/doc/b3d.md b/mods/modlib/doc/b3d.md deleted file mode 100644 index a591271c..00000000 --- a/mods/modlib/doc/b3d.md +++ /dev/null @@ -1,79 +0,0 @@ -# B3D Reader & Writer - -## `b3d.read(file)` - -Reads from `file`, which is expected to provide `file:read(nbytes)`. `file` is not closed. - -Returns a B3D model object. - -## `:write(file)` - -Writes the B3D model object `self` to `file`. - -`file` must provide `file:write(bytestr)`. It should be in binary mode. -It is not closed after writing. - -## `:write_string()` - -Writes the B3D model object to a bytestring, which is returned. - -## `:to_gltf()` - -Returns a glTF JSON representation of `self` in Lua table format. - -## `:write_gltf(file)` - -Convenience function to write the glTF representation to `file` using modlib's `json` writer. - -`file` must provide `file:write(str)`. It is not closed afterwards. - -## Examples - -### Converting B3D to glTF - -This example loops over all files in `dir_path`, converting them to glTFs which are stored in `out_dir_path`. - -```lua -local modpath = minetest.get_modpath(minetest.get_current_modname()) -local dir_path = modpath .. "/b3d" -local out_dir_path = modpath .. "/gltf" -for _, filename in ipairs(minetest.get_dir_list(dir_path, false --[[only files]])) do - -- First read the B3D - local in_file = assert(io.open(dir_path .. "/" .. filename, "rb")) - local model = assert(b3d.read(in_file)) - in_file:close() - -- Then write the glTF - local out_file = io.open(out_dir_path .. "/" .. filename .. ".gltf", "wb") - model:write_gltf(out_file) - out_file:close() -end -``` - -### [Round-trip (minifying B3Ds)](https://github.com/appgurueu/modlib_test/blob/f11c8e580e90454bc1adaa11a58e0c0217217d90/b3d.lua) - -This example from [`modlib_test`](https://github.com/appgurueu/modlib_test) reads, writes, and then reads again, -in order to verify that no data is lost through writing. - -Simply re-writing a model using modlib's B3D writer often reduces model sizes, -since for example modlib does not write `0` weights for bones. - -Keep in mind to use the `rb` and `wb` modes for I/O operations -to force Windows systems to not perform a line feed normalization. - -### [Extracting triangle sets](https://github.com/appgurueu/ghosts/blob/42a9eb9ee81fc6760a0278d23e4c47bc68bb4919/init.lua#L41-L79) - -The [Ghosts](https://github.com/appgurueu/ghosts/) mod extracts triangle sets using the B3D module -to then approximate the player shape using particles picked from these triangles. - -### [Animating the player](https://github.com/appgurueu/character_anim/blob/c48b282c0b42b32294ec2fddc03aa93141cbd894/init.lua#L213) - -[`character_anim`](https://github.com/appgurueu/character_anim/) uses the B3D module to determine the bone overrides required -for animating the player entirely Lua-side using bone overrides. - -### [Generating a Go board](https://github.com/appgurueu/go/blob/997ce85260d232a05dd668c32c6854bf34e3d5be/build/generate_models.lua) - -This example from the [Go](https://github.com/appgurueu/go) mod generates a Go board -where for each spot on the board there are two pieces (black and white), -both of which can be moved out of the board using a bone. - -It demonstrates how to use the writer (and how the table structure roughly looks like). diff --git a/mods/modlib/doc/b3d_specification.txt b/mods/modlib/doc/b3d_specification.txt deleted file mode 100644 index 81c86253..00000000 --- a/mods/modlib/doc/b3d_specification.txt +++ /dev/null @@ -1,260 +0,0 @@ -************************************************************************************ -* Blitz3d file format V0.01 * -************************************************************************************ - -This document and the information contained within is placed in the Public Domain. - -Please visit http://www.blitzbasic.co.nz for the latest version of this document. - -Please contact marksibly@blitzbasic.co.nz for more information and general inquiries. - - - -************************************************************************************ -* Introduction * -************************************************************************************ - -The Blitz3D file format specifies a format for storing texture, brush and entity descriptions for -use with the Blitz3D programming language. - -The rationale behind the creation of this format is to allow for the generation of much richer and -more complex Blitz3D scenes than is possible using established file formats - many of which do not -support key features of Blitz3D, and all of which miss out on at least some features! - -A Blitz3D (.b3d) file is split up into a sequence of 'chunks', each of which can contain data -and/or other chunks. - -Each chunk is preceded by an eight byte header: - -char tag[4] ;4 byte chunk 'tag' -int length ;4 byte chunk length (not including *this* header!) - -If a chunk contains both data and other chunks, the data always appears first and is of a fixed -length. - -A file parser should ignore unrecognized chunks. - -Blitz3D files are stored little endian (intel) style. - -Many aspects of the file format are not quite a 'perfect fit' for the way Blitz3D works. This has -been done mainly to keep the file format simple, and to make life easier for the authors of third -party importers/exporters. - - - -************************************************************************************ -* Chunk Types * -************************************************************************************ - -This lists the types of chunks that can appear in a b3d file, and the data they contain. - -Color values are always in the range 0 to 1. - -string (char[]) values are 'C' style null terminated strings. - -Quaternions are used to specify general orientations. The first value is the quaternion 'w' value, -the next 3 are the quaternion 'vector'. A 'null' rotation should be specified as 1,0,0,0. - -Anything that is referenced 'by index' always appears EARLIER in the file than anything that -references it. - -brush_id references can be -1: no brush. - -In the following descriptions, {} is used to signify 'repeating until end of chunk'. Also, a chunk -name enclosed in '[]' signifies the chunk is optional. - -Here we go! - - -BB3D - int version ;file format version: default=1 - [TEXS] ;optional textures chunk - [BRUS] ;optional brushes chunk - [NODE] ;optional node chunk - -The BB3D chunk appears first in a b3d file, and its length contains the rest of the file. - -Version is in major*100+minor format. To check the version, just divide by 100 and compare it with -the major version your software supports, eg: - -if file_version/100>my_version/100 - RuntimeError "Can't handle this file version!" -EndIf - -if file_version Mod 100>my_version Mod 100 - ;file is a more recent version, but should still be backwardly compatbile with what we can -handle! -EndIf - - -TEXS - { - char file[] ;texture file name - int flags,blend ;blitz3D TextureFLags and TextureBlend: default=1,2 - float x_pos,y_pos ;x and y position of texture: default=0,0 - float x_scale,y_scale ;x and y scale of texture: default=1,1 - float rotation ;rotation of texture (in radians): default=0 - } - -The TEXS chunk contains a list of all textures used in the file. - -The flags field value can conditional an additional flag value of '65536'. This is used to indicate that the texture uses secondary UV values, ala the TextureCoords command. Yes, I forgot about this one. - - -BRUS - int n_texs - { - char name[] ;eg "WATER" - just use texture name by default - float red,green,blue,alpha ;Blitz3D Brushcolor and Brushalpha: default=1,1,1,1 - float shininess ;Blitz3D BrushShininess: default=0 - int blend,fx ;Blitz3D Brushblend and BrushFX: default=1,0 - int texture_id[n_texs] ;textures used in brush - } - -The BRUS chunk contains a list of all brushes used in the file. - - -VRTS: - int flags ;1=normal values present, 2=rgba values present - int tex_coord_sets ;texture coords per vertex (eg: 1 for simple U/V) max=8 - int tex_coord_set_size ;components per set (eg: 2 for simple U/V) max=4 - { - float x,y,z ;always present - float nx,ny,nz ;vertex normal: present if (flags&1) - float red,green,blue,alpha ;vertex color: present if (flags&2) - float tex_coords[tex_coord_sets][tex_coord_set_size] ;tex coords - } - -The VRTS chunk contains a list of vertices. The 'flags' value is used to indicate how much extra -data (normal/color) is stored with each vertex, and the tex_coord_sets and tex_coord_set_size -values describe texture coordinate information stored with each vertex. - - -TRIS: - int brush_id ;brush applied to these TRIs: default=-1 - { - int vertex_id[3] ;vertex indices - } - -The TRIS chunk contains a list of triangles that all share a common brush. - - -MESH: - int brush_id ;'master' brush: default=-1 - VRTS ;vertices - TRIS[,TRIS...] ;1 or more sets of triangles - -The MESH chunk describes a mesh. A mesh only has one VRTS chunk, but potentially many TRIS chunks. - - -BONE: - { - int vertex_id ;vertex affected by this bone - float weight ;how much the vertex is affected - } - -The BONE chunk describes a bone. Weights are applied to the mesh described in the enclosing ANIM - -in 99% of cases, this will simply be the MESH contained in the root NODE chunk. - - -KEYS: - int flags ;1=position, 2=scale, 4=rotation - { - int frame ;where key occurs - float position[3] ;present if (flags&1) - float scale[3] ;present if (flags&2) - float rotation[4] ;present if (flags&4) - } - -The KEYS chunk is a list of animation keys. The 'flags' value describes what kind of animation -info is stored in the chunk - position, scale, rotation, or any combination of. - - -ANIM: - int flags ;unused: default=0 - int frames ;how many frames in anim - float fps ;default=60 - -The ANIM chunk describes an animation. - - -NODE: - char name[] ;name of node - float position[3] ;local... - float scale[3] ;coord... - float rotation[4] ;system... - [MESH|BONE] ;what 'kind' of node this is - if unrecognized, just use a Blitz3D -pivot. - [KEYS[,KEYS...]] ;optional animation keys - [NODE[,NODE...]] ;optional child nodes - [ANIM] ;optional animation - -The NODE chunk describes a Blitz3D Entity. The scene hierarchy is expressed by the nesting of NODE -chunks. - -NODE kinds are currently mutually exclusive - ie: a node can be a MESH, or a BONE, but not both! -However, it can be neither...if no kind is specified, the node is just a 'null' node - in Blitz3D -speak, a pivot. - -The presence of an ANIM chunk in a NODE indicates that an animation starts here in the hierarchy. -This allows animations of differing speeds/lengths to be potentially nested. - -There are many more 'kind' chunks coming, including camera, light, sprite, plane etc. For now, the -use of a Pivot in cases where the node kind is unknown will allow for backward compatibility. - - - -************************************************************************************ -* Examples * -************************************************************************************ - -A typical b3d file will contain 1 TEXS chunk, 1 BRUS chunk and 1 NODE chunk, like this: - -BB3D - 1 - TEXS - ...list of textures... - BRUS - ...list of brushes... - NODE - ...stuff in the node... - -A simple, non-animating, non-textured etc mesh might look like this: - -BB3D - 1 ;version - NODE - "root_node" ;node name - 0,0,0 ;position - 1,1,1 ;scale - 1,0,0,0 ;rotation - MESH ;the mesh - -1 ;brush: no brush - VRTS ;vertices in the mesh - 0 ;no normal/color info in verts - 0,0 ;no texture coords in verts - {x,y,z...} ;vertex coordinates - TRIS ;triangles in the mesh - -1 ;no brush for this triangle - {v0,v1,v2...} ;vertices - - -A more complex 'skinned mesh' might look like this (only chunks shown): - -BB3D - TEXS ;texture list - BRUS ;brush list - NODE ;root node - MESH ;mesh - the 'skin' - ANIM ;anim - NODE ;first child of root node - eg: "pelvis" - BONE ;vertex weights for pelvis - KEYS ;anim keys for pelvis - NODE ;first child of pelvis - eg: "left-thigh" - BONE ;bone - KEYS ;anim keys for left-thigh - NODE ;second child of pelvis - eg: "right-thigh" - BONE ;vertex weights for right-thigh - KEYS ;anim keys for right-thigh - -...and so on. diff --git a/mods/modlib/doc/bluon.md b/mods/modlib/doc/bluon.md deleted file mode 100644 index 71276879..00000000 --- a/mods/modlib/doc/bluon.md +++ /dev/null @@ -1,132 +0,0 @@ -# Bluon - -Binary Lua object notation. - -## `new(def)` - -```lua -def = { - aux_is_valid = function(object) - return is_valid - end, - aux_len = function(object) - return length_in_bytes - end, - -- read type byte, stream providing :read(count), map of references -> id - aux_read = function(type, stream, references) - ... = stream:read(...) - return object - end, - -- object to be written, stream providing :write(text), list of references - aux_write = function(object, stream, references) - stream:write(...) - end -} -``` - -## `:is_valid(object)` - -Returns whether the given object can be represented by the instance as boolean. - -## `:len(object)` - -Returns the expected length of the object if serialized by the current instance in bytes. - -## `:write(object, stream)` - -Writes the object to a stream supporting `:write(text)`. Throws an error if invalid. - -## `:read(stream)` - -Reads a single bluon object from a stream supporting `:read(count)`. Throws an error if invalid bluon. - -Checking whether the stream has been fully consumed by doing `assert(not stream:read(1))` is left up to the user. - -## Format - -Bluon uses a "tagged union" binary format: -Values are stored as a one-byte tag followed by the contents of the union. -For frequently used "constants", only a tag is used. - -`nil` is an exception; since it can't appear in tables, it gets no tag. -If the value to be written by Bluon is `nil`, Bluon simply writes *nothing*. - -The following is an enumeration of tag numbers, which are assigned *in this order*. - -* `false`: 0 -* `true`: 1 -* Numbers: - * Constants: 0, nan, +inf, -inf - * Integers: Little endian: - * Unsigned: `U8`, `U16`, `U32`, `U64` - * Negative: `-U8`, `-U16`, `-U32`, `-U64` - * Floats: Little endian `F32`, `F64` -* Strings: - * Constant: `""` - * Length is written as unsigned integer according to the tag: `S8`, `S16`, `S32`, `S64` - * followed by the raw bytes -* Tables: - * Tags: `M0`, `M8`, `M16`, `M32`, `M64` times `L0`, `L8`, `L16`, `L32`, `L64` - * `M` is more significant than `L`: The order of the cartesian product is `M0L0`, `M0L1`, ... - * List and map part count encoded as unsigned integers according to the tag, - list part count comes first - * followed by all values in the list part written as Bluon - * followed by all key-value pairs in the map part written as Bluon - (first the key is written as Bluon, then the value) -* Reference: - * Reference ID as unsigned integer: `R8`, `R16`, `R32`, `R64` - * References a previously encountered table or string by an index: - All tables and strings are numbered in the order they occur in the Bluon -* Reserved tags: - * All tags <= 55 are reserved. This gives 200 free tags. - -## Features - -* Embeddable: Written in pure Lua -* Storage efficient: No duplication of strings or reference-equal tables -* Flexible: Can serialize circular references and strings containing null - -## Simple example - -```lua -local object = ... --- Write to file -local file = io.open(..., "wb") -modlib.bluon:write(object, file) -file:close() --- Write to text -local rope = modlib.table.rope{} -modlib.bluon:write(object, rope) -text = rope:to_text() --- Read from text -local inputstream = modlib.text.inputstream"\1" -assert(modlib.bluon:read(object, rope) == true) -``` - -## Advanced example - -```lua --- Serializes all userdata to a constant string: -local custom_bluon = bluon.new{ - aux_is_valid = function(object) - return type(object) == "userdata" - end, - aux_len = function(object) - return 1 + ("userdata"):len()) - end, - aux_read = function(type, stream, references) - assert(type == 100, "unsupported type") - assert(stream:read(("userdata"):len()) == "userdata") - return userdata() - end, - -- object to be written, stream providing :write(text), list of references - aux_write = function(object, stream, references) - assert(type(object) == "userdata") - stream:write"\100userdata" - end -} --- Write to text -local rope = modlib.table.rope{} -custom_bluon:write(userdata(), rope) -assert(rope:to_text() == "\100userdata") -``` diff --git a/mods/modlib/doc/irr_obj_spec.md b/mods/modlib/doc/irr_obj_spec.md deleted file mode 100644 index f2bb1d52..00000000 --- a/mods/modlib/doc/irr_obj_spec.md +++ /dev/null @@ -1,76 +0,0 @@ -# Minetest Wavefront `.obj` file format specification - -Minetest Wavefront `.obj` is a subset of [Wavefront `.obj`](http://paulbourke.net/dataformats/obj/). - -It is inferred from the [Minetest Irrlicht `.obj` reader](https://github.com/minetest/irrlicht/blob/master/source/Irrlicht/COBJMeshFileLoader.cpp). - -`.mtl` files are not supported since Minetest's media loading process ignores them due to the extension. - -## Lines / "Commands" - -Irrlicht only looks at the first characters needed to tell commands apart (imagine a prefix tree of commands). - -Superfluous parameters are ignored. - -Numbers are formatted as either: - -* Float: An optional minus sign (`-`), one or more decimal digits, followed by the decimal dot (`.`) then again one or more digits -* Integer: An optional minus sign (`-`) followed by one or more decimal digits - -Indexing starts at one. Indices are formatted as integers. Negative indices relative to the end of a buffer are supported. - -* Comments: `# ...`; unsupported commands are silently ignored as well -* Groups: `g ` or `usemtl ` - * Subsequent faces belong to a new group / material, no matter the supplied names - * Each group gets their own material (texture); indices are determined by order of appearance - * Empty groups (groups without faces) are ignored -* Vertices (all numbers): `v `, global to the model -* Texture Coordinates (all numbers): `vt `, global to the model -* Normals (all numbers): `vn `, global to the model -* Faces (all vertex/texcoord/normal indices); always local to the current group: - * `f ` - * `f / / ... /` - * `f // / ... /` - * `f // // ... //` - -## Coordinate system orientation ("handedness") - -Vertex & normal X-coordinates are inverted ($x' = -x$); -texture Y-coordinates are inverted as well ($y' = 1 - y$). - -## Example - -```obj -# A simple 2³ cube centered at the origin; each face receives a separate texture / tile -# no care was taken to ensure "proper" texture orientation -v -1 -1 -1 -v -1 -1 1 -v -1 1 -1 -v -1 1 1 -v 1 -1 -1 -v 1 -1 1 -v 1 1 -1 -v 1 1 1 -vn -1 0 0 -vn 0 -1 0 -vn 0 0 -1 -vn 1 0 0 -vn 0 1 0 -vn 0 0 1 -vt 0 0 -vt 1 0 -vt 0 1 -vt 1 1 -g negative_x -f 1/1/1 3/3/1 2/2/1 4/4/1 -g negative_y -f 1/1/2 5/3/2 2/2/2 6/4/2 -g negative_z -f 1/1/3 5/3/3 3/2/3 7/4/3 -g positive_x -f 5/1/4 7/3/4 2/2/4 8/4/4 -g positive_y -f 3/1/5 7/3/5 4/2/5 8/4/5 -g positive_z -f 2/1/6 6/3/6 4/2/6 8/4/6 -``` diff --git a/mods/modlib/doc/json.md b/mods/modlib/doc/json.md deleted file mode 100644 index 1c2969b6..00000000 --- a/mods/modlib/doc/json.md +++ /dev/null @@ -1,9 +0,0 @@ -# JSON - -Advantages over `minetest.write_json`/`minetest.parse_json`: - -* Twice as fast in most benchmarks (for pre-5.6 at least) -* Uses streams instead of strings -* Customizable -* Useful error messages -* Pure Lua diff --git a/mods/modlib/doc/minetest/conf.md b/mods/modlib/doc/minetest/conf.md deleted file mode 100644 index d2754ccf..00000000 --- a/mods/modlib/doc/minetest/conf.md +++ /dev/null @@ -1,31 +0,0 @@ -# Configuration - -## Legacy - -1. Configuration is loaded from `/config/.`, the following extensions are supported and loaded (in the given order), with loaded configurations overriding properties of previous ones: - 1. [`json`](https://json.org) - 2. [`lua`](https://lua.org) - 3. [`luon`](https://github.com/appgurueu/luon), Lua but without the `return` - 4. [`conf`](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt) -2. Settings are loaded from `minetest.conf` and override configuration values - -## Locations - -0. Default configuration: `/conf.lua` -1. World configuration: `config/.` -2. Mod configuration: `/conf.` -3. Minetest configuration: `minetest.conf` - -## Formats - -1. [`lua`](https://lua.org) - * Lua, with the environment being the configuration object - * `field = value` works - * Return new configuration object to replace -2. [`luon`](https://github.com/appgurueu/luon) - * Single Lua literal - * Booleans, numbers, strings and tables -3. [`conf`](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt) - * Minetest-like configuration files -4. [`json`](https://json.org) - * Not recommended diff --git a/mods/modlib/doc/minetest/schematic.md b/mods/modlib/doc/minetest/schematic.md deleted file mode 100644 index 56ea4eb2..00000000 --- a/mods/modlib/doc/minetest/schematic.md +++ /dev/null @@ -1,79 +0,0 @@ -# Schematic - -A schematic format with support for metadata and baked light data. - -## Table Format - -The table format uses a table with the following mandatory fields: - -* `size`: Size of the schematic in nodes, vector -* `node_names`: List of node names -* `nodes`: List of node indices (into the `node_names` table) -* `param2s`: List of node `param2` values (numbers) - -and the following optional fields: - -* `light_values`: List of node `param1` (light) values (numbers) -* `metas`: Map from indices in the cuboid to metadata tables as produced by `minetest.get_meta(pos):to_table()` - -A "vector" is a table with fields `x`, `y`, `z` for the 3 coordinates. - -The `nodes`, `param2s` and `light_values` lists are in the order dictated by `VoxelArea:iterp` (Z-Y-X). - -The cuboid indices for the `metas` table are calculated as `(z * size.y) + y * size.x + x` where `x`, `y`, `z` are relative to the min pos of the cuboid. - -## Binary Format - -The binary format uses modlib's Bluon to write the table format. - -Since `param2s` (and optionally `light_values`) are all bytes, they are converted from lists of numbers to (byte)strings before writing. - -For uncompressed files, it uses `MLBS` (short for "ModLib Bluon Schematic") for the magic bytes, -followed by the raw Bluon binary data. - -For compressed files, it uses `MLZS` (short for "ModLib Zlib-compressed Schematic") for the magic bytes, -followed by the zlib-compressed Bluon binary data. - -## API - -### `schematic.setmetatable(obj)` - -Sets the metatable of a table `obj` to the schematic metatable. -Useful if you've deserialized a schematic or want to create a schematic from the table format. - -### `schematic.create(params, pos_min, pos_max)` - -Creates a schematic from a map cuboid - -* `params`: Table with fields - * `metas` (default `true`): Whether to store metadata - * `light_values`: Whether to bake light values (`param1`). - Usually not recommended, default `false`. -* `pos_min`: Minimum position of the cuboid, inclusive -* `pos_max`: Maximum position of the cuboid, inclusive - -### `schematic:place(pos_min)` - -"Inverse" to `schematic.create`: Places the schematic `self` starting at `pos_min`. - -Content IDs (nodes), param1s, param2s, and metadata in the area will be completely erased and replaced; if light data is present, param1s will simply be set, otherwise they will be recalculated. - -### `schematic:write_zlib_bluon(path)` - -Write a binary file containing the schematic in *zlib-compressed* binary format to `path`. -**You should generally prefer this over `schematic:write_bluon`: zlib compression comes with massive size reductions.** - -### `schematic.read_zlib_bluon(path)` - -"Inverse": Read a binary file containing a schematic in *zlib-compressed* binary format from `path`, returning a `schematic` instance. -**You should generally prefer this over `schematic.read_bluon`: zlib compression comes with massive size reductions.** - -### `schematic:write_bluon(path)` - -Write a binary file containing the schematic in uncompressed binary format to `path`. -Useful only if you want to eliminate the time spent compressing. - -### `schematic.read_bluon(path)` - -"Inverse": Read a binary file containing a schematic in uncompressed binary format from `path`, returning a `schematic` instance. -Useful only if you want to eliminate the time spent decompressing. diff --git a/mods/modlib/doc/minetest/texmod.md b/mods/modlib/doc/minetest/texmod.md deleted file mode 100644 index b50235b4..00000000 --- a/mods/modlib/doc/minetest/texmod.md +++ /dev/null @@ -1,39 +0,0 @@ -# Texture Modifiers - -## Specification - -Refer to the following "specifications", in this order of precedence: - -1. [Minetest Docs](https://github.com/minetest/minetest_docs/blob/master/doc/texture_modifiers.adoc) -2. [Minetest Lua API](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt), section "texture modifiers" -3. [Minetest Sources](https://github.com/minetest/minetest/blob/master/src/client/tile.cpp) - -## Implementation - -### Constructors ("DSL") - -Constructors are kept close to the original forms and perform basic validation. Additionally, texture modifiers can directly be created using `texmod{type = "...", ...}`, bypassing the checks. - -### Writing - -The naive way to implement string building would be to have a -`tostring` function recursively `tostring`ing the sub-modifiers of the current modifier; -each writer would only need a stream (often passed in the form of a `write` function). - -The problem with this is that applying escaping quickly makes this run in quadratic time. - -A more efficient approach passes the escaping along with the `write` function. Thus a "writer" object `w` encapsulating this state is passed around. - -The writer won't necessarily produce the *shortest* or most readable texture modifier possible; for example, colors will be converted to hexadecimal representation, and texture modifiers with optional parameters may have the default values be written. -You should not rely on the writer to produce any particular of the various valid outputs. - -### Reading - -**The reader does not attempt to precisely match the behavior of Minetest's shotgun "parser".** It *may* be more strict in some instances, rejecting insane constructs Minetest's parser allows. -It *may* however sometimes also be more lenient (though I haven't encountered an instance of this yet), accepting sane constructs which Minetest's parser rejects due to shortcomings in its implementation. - -The parser is written *to spec*, in the given order of precedence. -If a documented construct is not working, that's a bug. If a construct which is incorrect according to the docs is accepted, that's a bug too. -Compatibility with Minetest's parser for all reasonable inputs is greatly valued. If an invalid input is notably used in the wild (or it is reasonable that it may occur in the wild) and supported by Minetest, this parser ought to support it too. - -Recursive descent parsing is complicated by the two forms of escaping texture modifiers support: Reading each character needs to handle escaping. The current depth and whether the parser is inside an inventorycube need to be saved in state variables. These could be passed on the stack, but it's more comfortable (and possibly more efficient) to just share them across all functions and restore them after leaving an inventorycube / moving to a lower level. diff --git a/mods/modlib/doc/persistence/lua_log_file.md b/mods/modlib/doc/persistence/lua_log_file.md deleted file mode 100644 index 44cfd000..00000000 --- a/mods/modlib/doc/persistence/lua_log_file.md +++ /dev/null @@ -1,23 +0,0 @@ -# Lua Log Files - -A data log file based on Lua statements. High performance. Example from `test.lua`: - -```lua -local logfile = persistence.lua_log_file.new(mod.get_resource"logfile.test.lua", {}) -logfile:init() -logfile.root = {} -logfile:rewrite() -logfile:set_root({a = 1}, {b = 2, c = 3}) -logfile:close() -logfile:init() -assert(table.equals(logfile.root, {[{a = 1}] = {b = 2, c = 3}})) -``` - -Both strings and tables are stored in a reference table. Unused strings won't be garbage collected as Lua doesn't allow marking them as weak references. -This means that setting lots of temporary strings will waste memory until you call `:rewrite()` on the log file. An alternative is to set the third parameter, `reference_strings`, to `false` (default value is `true`): - -```lua -persistence.lua_log_file.new(mod.get_resource"logfile.test.lua", {}, false) -``` - -This will prevent strings from being referenced, possibly bloating file size, but saving memory. diff --git a/mods/modlib/doc/persistence/sqlite3.md b/mods/modlib/doc/persistence/sqlite3.md deleted file mode 100644 index cd03bd67..00000000 --- a/mods/modlib/doc/persistence/sqlite3.md +++ /dev/null @@ -1,41 +0,0 @@ -# SQLite3 Database Persistence - -Uses a SQLite3 database to persistently store a Lua table. Obtaining it is a bit trickier, as it requires access to the `lsqlite3` library, which may be passed: - -```lua -local modlib_sqlite3 = persistence.sqlite3(require"lsqlite3") -``` - -(assuming `require` is that of an insecure environment if Minetest is used) - -Alternatively, if you are not running Minetest, mod security is disabled, you have (temporarily) provided `require` globally, or added `modlib` to `secure.trusted_mods`, you can simply do the following: - -```lua -local modlib_sqlite3 = persistence.sqlite3() -``` - -Modlib will then simply call `require"lsqlite3"` for you. - -Then, you can proceed to create a new database: - -```lua -local database = persistence.modlib_sqlite3.new(mod.get_resource"database.test.sqlite3", {}) --- Create or load -database:init() --- Use it -database:set_root("key", {nested = true}) -database:close() -``` - -It uses a similar API to Lua log files: - -* `new(filename, root)` - without `reference_strings` however (strings aren't referenced currently) -* `init` -* `set` -* `set_root` -* `rewrite` -* `close` - -The advantage over Lua log files is that the SQlite3 database keeps disk usage minimal. Unused tables are dropped from the database immediately through reference counting. The downside of this is that this, combined with the overhead of using SQLite3, of course takes time, making updates on the SQLite3 database slower than Lua log file updates (which just append to an append-only file). -As simple and fast reference counting doesn't handle cycles, an additional `collectgarbage` stop-the-world method performing a full garbage collection on the database is provided which is called during `init`. -The method `defragment_ids` should not have to be used in practice (if it has to be, it happens automatically) and should be used solely for debugging purposes (neater IDs). diff --git a/mods/modlib/doc/schema.md b/mods/modlib/doc/schema.md deleted file mode 100644 index 82f776c0..00000000 --- a/mods/modlib/doc/schema.md +++ /dev/null @@ -1,47 +0,0 @@ -# Schema - -Place a file `schema.lua` in your mod, returning a schema table. - -## Non-string entries and `minetest.conf` - -Suppose you have the following schema: - -```lua -return { - type = "table", - entries = { - [42] = { - type = "boolean", - description = "The Answer" - default = true - } - } -} -``` - -And a user sets the following config: - -```conf -mod.42 = false -``` - -It won't work, as the resulting table will be `{["42"] = false}` instead of `{[42] = false}`. In order to make this work, you have to convert the keys yourself: - -```lua -return { - type = "table", - keys = { - -- this will convert all keys to numbers - type = "number" - }, - entries = { - [42] = { - type = "boolean", - description = "The Answer" - default = true - } - } -} -``` - -This is best left explicit. First, you shouldn't be using numbered field keys if you want decent `minetest.conf` support, and second, `modlib`'s schema module could only guess in this case, attempting conversion to number / boolean. What if both number and string field were set as possible entries? Should the string field be deleted? And so on. diff --git a/mods/modlib/file.lua b/mods/modlib/file.lua deleted file mode 100644 index 9c093c83..00000000 --- a/mods/modlib/file.lua +++ /dev/null @@ -1,164 +0,0 @@ -local dir_delim = ... --- Localize globals -local assert, io, minetest, modlib, string, pcall = assert, io, minetest, modlib, string, pcall - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - -_ENV.dir_delim = dir_delim - -function get_name(filepath) - return filepath:match("([^%" .. dir_delim .. "]+)$") or filepath -end - -function split_extension(filename) - return filename:match"^(.*)%.(.*)$" -end ---! deprecated -get_extension = split_extension - -function split_path(filepath) - return modlib.text.split_unlimited(filepath, dir_delim, true) -end - --- concat_path is set by init.lua to avoid code duplication - --- Lua 5.4 has `` for this, but we're restricted to 5.1, --- so we need to roll our own `try f = io.open(...); return func(f) finally f:close() end`. -function with_open(filename, mode, func --[[function(file), called with `file = io.open(filename, mode)`]]) - local file = assert(io.open(filename, mode or "r")) - -- Throw away the stacktrace. The alternative would be to use `xpcall` - -- to bake the stack trace into the error string using `debug.traceback`. - -- Lua will have prepended `::` already however. - return (function(status, ...) - file:close() - assert(status, ...) - return ... - end)(pcall(func, file)) -end - -function read(filename) - local file, err = io.open(filename, "r") - if file == nil then return nil, err end - local content = file:read"*a" - file:close() - return content -end - -function read_binary(filename) - local file, err = io.open(filename, "rb") - if file == nil then return nil, err end - local content = file:read"*a" - file:close() - return content -end - -function write_unsafe(filename, new_content) - local file, err = io.open(filename, "w") - if file == nil then return false, err end - file:write(new_content) - file:close() - return true -end - -write = minetest and minetest.safe_file_write or write_unsafe - -function write_binary_unsafe(filename, new_content) - local file, err = io.open(filename, "wb") - if file == nil then return false, err end - file:write(new_content) - file:close() - return true -end - -write_binary = minetest and minetest.safe_file_write or write_binary_unsafe - -function ensure_content(filename, ensured_content) - local content = read(filename) - if content ~= ensured_content then - return write(filename, ensured_content) - end - return true -end - -function append(filename, new_content) - local file, err = io.open(filename, "a") - if file == nil then return false, err end - file:write(new_content) - file:close() - return true -end - -function exists(filename) - local file, err = io.open(filename, "r") - if file == nil then return false, err end - file:close() - return true -end - -function create_if_not_exists(filename, content) - if not exists(filename) then - return write(filename, content or "") - end - return false -end - -function create_if_not_exists_from_file(filename, src_filename) return create_if_not_exists(filename, read(src_filename)) end - -if not minetest then return end - --- Process Bridge Helpers -process_bridges = {} - -function process_bridge_build(name, input, output, logs) - if not input or not output or not logs then - minetest.mkdir(minetest.get_worldpath() .. "/bridges/" .. name) - end - input = input or minetest.get_worldpath() .. "/bridges/" .. name .. "/input.txt" - output = output or minetest.get_worldpath() .. "/bridges/" .. name .. "/output.txt" - logs = logs or minetest.get_worldpath() .. "/bridges/" .. name .. "/logs.txt" - -- Clear input - write(input, "") - -- Clear output - write(output, "") - -- Create logs if not exists - create_if_not_exists(logs, "") - process_bridges[name] = { - input = input, - output = output, - logs = logs, - output_file = io.open(output, "a") - } -end - -function process_bridge_listen(name, line_consumer, step) - local bridge = process_bridges[name] - modlib.minetest.register_globalstep(step or 0.1, function() - for line in io.lines(bridge.input) do - line_consumer(line) - end - write(bridge.input, "") - end) -end - -function process_bridge_serve(name, step) - local bridge = process_bridges[name] - modlib.minetest.register_globalstep(step or 0.1, function() - bridge.output_file:close() - process_bridges[name].output_file = io.open(bridge.output, "a") - end) -end - -function process_bridge_write(name, message) - local bridge = process_bridges[name] - bridge.output_file:write(message .. "\n") -end - -function process_bridge_start(name, command, os_execute) - local bridge = process_bridges[name] - os_execute(string.format(command, bridge.output, bridge.input, bridge.logs)) -end - --- Export environment -return _ENV diff --git a/mods/modlib/func.lua b/mods/modlib/func.lua deleted file mode 100644 index cb01e0b1..00000000 --- a/mods/modlib/func.lua +++ /dev/null @@ -1,126 +0,0 @@ --- Localize globals -local modlib, unpack, select, setmetatable - = modlib, unpack, select, setmetatable - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - -function no_op() end - -function identity(...) return ... end - --- TODO switch all of these to proper vargs - -function curry(func, ...) - local args = { ... } - return function(...) return func(unpack(args), ...) end -end - -function curry_tail(func, ...) - local args = { ... } - return function(...) return func(unpack(modlib.table.concat({...}, args))) end -end - -function curry_full(func, ...) - local args = { ... } - return function() return func(unpack(args)) end -end - -function args(...) - local args = { ... } - return function(func) return func(unpack(args)) end -end - -function value(val) return function() return val end end - -function values(...) - local args = { ... } - return function() return unpack(args) end -end - -function memoize(func) - return setmetatable({}, { - __index = function(self, key) - local value = func(key) - self[key] = value - return value - end, - __call = function(self, arg) - return self[arg] - end, - __mode = "k" - }) -end - -function compose(func, other_func) - return function(...) - return func(other_func(...)) - end -end - -function override_chain(func, override) - return function(...) - func(...) - return override(...) - end -end - ---+ Calls func using the provided arguments, deepcopies all arguments -function call_by_value(func, ...) - return func(unpack(modlib.table.deepcopy{...}, 1, select("#", ...))) -end - --- Functional wrappers for Lua's builtin metatable operators (arithmetic, concatenation, length, comparison, indexing, call) - --- TODO (?) add operator table `["+"] = add, ...` - -function add(a, b) return a + b end - -function sub(a, b) return a - b end - -function mul(a, b) return a * b end - -function div(a, b) return a / b end - -function mod(a, b) return a % b end - -function pow(a, b) return a ^ b end - -function unm(a) return -a end - -function concat(a, b) return a .. b end - -function len(a) return #a end - -function eq(a, b) return a == b end - -function neq(a, b) return a ~= b end - -function lt(a, b) return a < b end - -function gt(a, b) return a > b end - -function le(a, b) return a <= b end - -function ge(a, b) return a >= b end - -function index(object, key) return object[key] end - -function newindex(object, key, value) object[key] = value end - -function call(object, ...) object(...) end - --- Functional wrappers for logical operators, suffixed with _ for syntactical convenience - -function not_(a) return not a end -_ENV["not"] = not_ - -function and_(a, b) return a and b end -_ENV["and"] = and_ - -function or_(a, b) return a or b end -_ENV["or"] = or_ - --- Export environment -return _ENV \ No newline at end of file diff --git a/mods/modlib/hashheap.lua b/mods/modlib/hashheap.lua deleted file mode 100644 index 2b4e346c..00000000 --- a/mods/modlib/hashheap.lua +++ /dev/null @@ -1,117 +0,0 @@ --- Localize globals -local assert, math_floor, setmetatable, table_insert = assert, math.floor, setmetatable, table.insert - --- Set environment --- Min. heap + Lua hash table to allow updating the stored values -local _ENV = {} -setfenv(1, _ENV) - -local metatable = { __index = _ENV } - -function less_than(a, b) - return a < b -end - ---> empty, duplicate-free min heap with priority queue functionality -function new(less_than) - return setmetatable({ less_than = less_than, indices = {} }, metatable) -end - -local function swap(self, child_index, parent_index) - local child_value, parent_value = self[child_index], self[parent_index] - self.indices[parent_value], self.indices[child_value] = child_index, parent_index - self[parent_index], self[child_index] = child_value, parent_value -end - -local function heapify_up(self, index) - if index == 1 then - return - end - local parent_index = math_floor(index / 2) - if self.less_than(self[index], self[parent_index]) then - swap(self, index, parent_index) - heapify_up(self, parent_index) - end -end - -local function heapify_down(self, index) - local left_child = index * 2 - if left_child > #self then - return - end - local smallest_child = left_child + 1 - if smallest_child > #self or self.less_than(self[left_child], self[smallest_child]) then - smallest_child = left_child - end - if self.less_than(self[smallest_child], self[index]) then - swap(self, smallest_child, index) - heapify_down(self, smallest_child) - end -end - -function push(self, value) - table_insert(self, value) - local last = #self - self.indices[value] = last - heapify_up(self, last) -end - -function top(self) - return self[1] -end - --- TODO what if empty? -function pop(self) - local value = self[1] - self.indices[value] = nil - local last = #self - if last == 1 then - self[1] = nil - return value - end - self[1], self[last] = self[last], nil - heapify_down(self, 1) - return value -end - -function find_index(self, element) - return self.indices[element] -end - --- Notify heap that the element has been decreased -function decrease(self, element) - heapify_up(self, assert(self:find_index(element))) -end - --- Notify heap that the element has been increased -function increase(self, element) - heapify_down(self, assert(self:find_index(element))) -end - --- Replaces the specified element - by identity - with the new element -function replace(self, element, new_element) - local index = assert(self:find_index(element)) - assert(self:find_index(new_element) == nil, "no duplicates allowed") - self[index] = new_element - self.indices[element] = nil - self.indices[new_element] = index; - (self.less_than(new_element, element) and heapify_up or heapify_down)(self, index) -end - -function remove(self, element) - local index = assert(self:find_index(element), "element not found") - self.indices[element] = nil - if index == #self then - self[index] = nil - else - local last_index = #self - local last_element = self[last_index] - self[last_index] = nil - self[index] = last_element - self.indices[last_element] = index; - (self.less_than(last_element, element) and heapify_up or heapify_down)(self, index) - end -end - --- Export environment -return _ENV diff --git a/mods/modlib/hashlist.lua b/mods/modlib/hashlist.lua deleted file mode 100644 index 832e4c69..00000000 --- a/mods/modlib/hashlist.lua +++ /dev/null @@ -1,93 +0,0 @@ --- Localize globals -local setmetatable = setmetatable - --- Table based list, can handle at most 2^52 pushes -local list = {} --- TODO use __len for Lua version > 5.1 -local metatable = {__index = list} -list.metatable = metatable - --- Takes a list -function list:new() - self.head = 0 - self.length = #self - return setmetatable(self, metatable) -end - -function list:in_bounds(index) - return index >= 1 and index <= self.length -end - -function list:get(index) - return self[self.head + index] -end - -function list:set(index, value) - assert(value ~= nil) - self[self.head + index] = value -end - -function list:len() - return self.length -end - -function list:ipairs() - local index = 0 - return function() - index = index + 1 - if index > self.length then - return - end - return index, self[self.head + index] - end -end - -function list:rpairs() - local index = self.length + 1 - return function() - index = index - 1 - if index < 1 then - return - end - return index, self[self.head + index] - end -end - -function list:push_tail(value) - assert(value ~= nil) - self.length = self.length + 1 - self[self.head + self.length] = value -end - -function list:get_tail() - return self[self.head + self.length] -end - -function list:pop_tail() - if self.length == 0 then return end - local value = self:get_tail() - self[self.head + self.length] = nil - self.length = self.length - 1 - return value -end - -function list:push_head(value) - self[self.head] = value - self.head = self.head - 1 - self.length = self.length + 1 -end - -function list:get_head() - return self[self.head + 1] -end - -function list:pop_head() - if self.length == 0 then return end - local value = self:get_head() - self.length = self.length - 1 - self.head = self.head + 1 - self[self.head] = nil - return value -end - -return list \ No newline at end of file diff --git a/mods/modlib/heap.lua b/mods/modlib/heap.lua deleted file mode 100644 index 5bdd7c29..00000000 --- a/mods/modlib/heap.lua +++ /dev/null @@ -1,60 +0,0 @@ --- Localize globals -local math_floor, setmetatable, table_insert = math.floor, setmetatable, table.insert - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - -local metatable = {__index = _ENV} - -function less_than(a, b) return a < b end - ---> empty min heap -function new(less_than) - return setmetatable({less_than = less_than}, metatable) -end - -function push(self, value) - table_insert(self, value) - local function heapify(index) - if index == 1 then - return - end - local parent = math_floor(index / 2) - if self.less_than(self[index], self[parent]) then - self[parent], self[index] = self[index], self[parent] - heapify(parent) - end - end - heapify(#self) -end - -function pop(self) - local value = self[1] - local last = #self - if last == 1 then - self[1] = nil - return value - end - self[1], self[last] = self[last], nil - last = last - 1 - local function heapify(index) - local left_child = index * 2 - if left_child > last then - return - end - local smallest_child = left_child + 1 - if smallest_child > last or self.less_than(self[left_child], self[smallest_child]) then - smallest_child = left_child - end - if self.less_than(self[smallest_child], self[index]) then - self[index], self[smallest_child] = self[smallest_child], self[index] - heapify(smallest_child) - end - end - heapify(1) - return value -end - --- Export environment -return _ENV \ No newline at end of file diff --git a/mods/modlib/init.lua b/mods/modlib/init.lua deleted file mode 100644 index de53983a..00000000 --- a/mods/modlib/init.lua +++ /dev/null @@ -1,147 +0,0 @@ -local modules = {} -for _, file in pairs{ - "schema", - "file", - "func", - "less_than", - "iterator", - "math", - "table", - "vararg", - "text", - "utf8", - "vector", - "matrix4", - "quaternion", - "trie", - "kdtree", - "hashlist", - "hashheap", - "heap", - "binary", - "b3d", - "json", - "luon", - "bluon", - "base64", - "persistence", - "debug", - "web", - "tex" -} do - modules[file] = file -end -if minetest then - modules.minetest = "minetest" -end - --- modlib.mod is an alias for modlib.minetest.mod -modules.string = "text" -modules.number = "math" - -local parent_dir -if not minetest then - -- TOFIX - local init_path = arg and arg[0] - parent_dir = init_path and init_path:match"^.[/\\]" or "" -end - -local dir_delim = rawget(_G, "DIR_DELIM") -- Minetest - or (rawget(_G, "package") and package.config and assert(package.config:match("^(.-)[\r\n]"))) or "/" - -local function concat_path(path) - return table.concat(path, dir_delim) -end - --- only used if Minetest is available -local function get_resource(modname, resource, ...) - if not resource then - resource = modname - modname = minetest.get_current_modname() - end - return concat_path{minetest.get_modpath(modname), resource, ...} -end - -local function load_module(self, module_name_or_alias) - local module_name = modules[module_name_or_alias] - if not module_name then - -- no such module - return - end - local environment - if module_name ~= module_name_or_alias then - -- alias handling - environment = self[module_name] - else - environment = dofile(minetest - and get_resource(self.modname, module_name .. ".lua") - or (parent_dir .. module_name .. ".lua")) - end - self[module_name_or_alias] = environment - return environment -end - -local rawget, rawset = rawget, rawset -modlib = setmetatable({}, { __index = load_module }) - --- TODO bump on release -modlib.version = 102 - -if minetest then - modlib.modname = minetest.get_current_modname() -end - --- Raw globals -modlib._RG = setmetatable({}, { - __index = function(_, index) - return rawget(_G, index) - end, - __newindex = function(_, index, value) - return rawset(_G, index, value) - end -}) - --- Globals merged with modlib -modlib.G = setmetatable({}, {__index = function(self, module_name) - local module = load_module(self, module_name) - if module == nil then - return _G[module_name] - end - if _G[module_name] then - setmetatable(module, {__index = _G[module_name]}) - end - return module -end}) - --- "Imports" modlib by changing the environment of the calling function ---! This alters environments at the expense of performance. Use with caution. ---! Prefer localizing modlib library functions or API tables if possible. -function modlib.set_environment() - setfenv(2, setmetatable({}, {__index = modlib.G})) -end - --- Force load file module to pass dir_delim & to set concat_path -modlib.file = assert(loadfile(get_resource"file.lua"))(dir_delim) -modlib.file.concat_path = concat_path - -if minetest then - -- Force-loading of the minetest & mod modules - -- Also sets modlib.mod -> modlib.minetest.mod alias. - local ml_mt = modlib.minetest - ml_mt.mod.get_resource = get_resource - modlib.mod = ml_mt.mod - -- HACK force load minetest/gametime.lua to ensure that the globalstep is registered earlier than globalsteps of mods depending on modlib - dofile(get_resource(modlib.modname, "minetest", "gametime.lua")) - local ie = minetest.request_insecure_environment() - if ie then - -- Force load persistence namespace to pass insecure require - -- TODO currently no need to set _G.require, lsqlite3 loads no dependencies that way - modlib.persistence = assert(loadfile(get_resource"persistence.lua"))(ie.require) - end -end - --- Run build scripts --- dofile(modlib.mod.get_resource("modlib", "build", "html_entities.lua")) - --- TODO verify localizations suffice -return modlib diff --git a/mods/modlib/iterator.lua b/mods/modlib/iterator.lua deleted file mode 100644 index e5ff49f7..00000000 --- a/mods/modlib/iterator.lua +++ /dev/null @@ -1,311 +0,0 @@ ---[[ - Iterators are always the *last* argument(s) to all functions here, - which differs from other modules which take what they operate on as first argument. - This is because iterators consist of three variables - iterator function, state & control variable - - and wrapping them (using a table, a closure or the like) would be rather inconvenient. - Having them as the last argument allows to just pass in the three variables returned by functions such as `[i]pairs`. - Additionally, putting functions first - although syntactically inconvenient - is consistent with Python and Lisp. -]] - -local coroutine_create, coroutine_resume, coroutine_yield, coroutine_status, unpack, select - = coroutine.create, coroutine.resume, coroutine.yield, coroutine.status, unpack, select - -local identity, not_, add = modlib.func.identity, modlib.func.not_, modlib.func.add - ---+ For all functions which aggregate over single values, use modlib.table.ivalues - not ipairs - for lists! ---+ Otherwise they will be applied to the indices. -local iterator = {} - -function iterator.wrap(iterator, state, control_var) - local function update_control_var(...) - control_var = ... - return ... - end - return function() - return update_control_var(iterator(state, control_var)) - end -end -iterator.closure = iterator.wrap -iterator.make_stateful = iterator.wrap - -function iterator.filter(predicate, iterator, state, control_var) - local function _filter(...) - local cvar = ... - if cvar == nil then - return - end - if predicate(...) then - return ... - end - return _filter(iterator(state, cvar)) - end - return function(state, control_var) - return _filter(iterator(state, control_var)) - end, state, control_var -end - -function iterator.truthy(...) - return iterator.filter(identity, ...) -end - -function iterator.falsy(...) - return iterator.filter(not_, ...) -end - -function iterator.map(map_func, iterator, state, control_var) - local function _map(...) - control_var = ... -- update control var - if control_var == nil then return end - return map_func(...) - end - return function() - return _map(iterator(state, control_var)) - end -end - -function iterator.map_values(map_func, iterator, state, control_var) - local function _map_values(cvar, ...) - if cvar == nil then return end - return cvar, map_func(...) - end - return function(state, control_var) - return _map_values(iterator(state, control_var)) - end, state, control_var -end - --- Iterator must be restartable -function iterator.rep(times, iterator, state, control_var) - times = times or 1 - if times == 1 then - return iterator, state, control_var - end - local function _rep(cvar, ...) - if cvar == nil then - times = times - 1 - if times == 0 then return end - return _rep(iterator(state, control_var)) - end - return cvar, ... - end - return function(state, control_var) - return _rep(iterator(state, control_var)) - end, state, control_var -end - --- Equivalent to `for x, y, z in iterator, state, ... do callback(x, y, z) end` -function iterator.foreach(callback, iterator, state, ...) - local function loop(...) - if ... == nil then return end - callback(...) - return loop(iterator(state, ...)) - end - return loop(iterator(state, ...)) -end - -function iterator.for_generator(caller, ...) - local co = coroutine_create(function(...) - return caller(function(...) - return coroutine_yield(...) - end, ...) - end) - local args, n_args = {...}, select("#", ...) - return function() - if coroutine_status(co) == "dead" then - return - end - local function _iterate(status, ...) - if not status then - error((...)) - end - return ... - end - return _iterate(coroutine_resume(co, unpack(args, 1, n_args))) - end -end - -function iterator.range(from, to, step) - if not step then - if not to then - from, to = 1, from - end - step = 1 - end - - return function(_, current) - current = current + step - if current > to then - return - end - return current - end, nil, from - step -end - -function iterator.aggregate(binary_func, total, ...) - for value in ... do - total = binary_func(total, value) - end - return total -end - --- Like `iterator.aggregate`, but does not expect a `total` -function iterator.reduce(binary_func, iterator, state, control_var) - local total = iterator(state, control_var) - if total == nil then - return -- nothing if the iterator is empty - end - for value in iterator, state, total do - total = binary_func(total, value) - end - return total -end -iterator.fold = iterator.reduce - --- TODO iterator.find(predicate, iterator, state, control_var) - -function iterator.any(...) - for val in ... do - if val then return true end - end - return false -end - -function iterator.all(...) - for val in ... do - if not val then return false end - end - return true -end - -function iterator.min(less_than_func, ...) - local min - for value in ... do - if min == nil or less_than_func(value, min) then - min = value - end - end - return min -end - --- TODO iterator.max - -function iterator.empty(iterator, state, control_var) - return iterator(state, control_var) == nil -end - -function iterator.first(iterator, state, control_var) - return iterator(state, control_var) -end - -function iterator.last(iterator, state, control_var) - -- Storing a vararg in a table seems to be necessary: https://stackoverflow.com/questions/73914273/ - -- This could be optimized further for memory by keeping the same table across calls, - -- but that might cause issues with multiple coroutines calling this - local last, last_n = {}, 0 - - local function _last(...) - local cvar = ... - if cvar == nil then - return unpack(last, 1, last_n) - end - - -- Write vararg to table: Avoid the creation of a garbage table every iteration by reusing the same table - last_n = select("#", ...) - for i = 1, last_n do - last[i] = select(i, ...) - end - - return _last(iterator(state, cvar)) - end - - return _last(iterator(state, control_var)) -end - --- Converts a vararg starting with `nil` (end of loop control variable) into nothing -local function nil_to_nothing(...) - if ... == nil then return end - return ... -end - -function iterator.select(n, iterator, state, control_var) - for _ = 1, n - 1 do - control_var = iterator(state, control_var) - if control_var == nil then return end - end - -- Either all values returned by the n-th call iteration - -- or nothing if the iterator holds fewer than `n` values - return nil_to_nothing(iterator(state, control_var)) -end - -function iterator.limit(count, iterator, state, control_var) - return function(state, control_var) - count = count - 1 - if count < 0 then return end - return iterator(state, control_var) - end, state, control_var -end - -function iterator.count(...) - local count = 0 - for _ in ... do - count = count + 1 - end - return count -end - -function iterator.sum(...) - return iterator.aggregate(add, 0, ...) -end - -function iterator.average(...) - local count = 0 - local sum = 0 - for value in ... do - count = count + 1 - sum = sum + value - end - return sum / count -end - ---: ... **restartable** iterator --- A single pass method for calculating the standard deviation exists but is highly inaccurate -function iterator.standard_deviation(...) - local avg = iterator.average(...) - local count = 0 - local sum = 0 - for value in ... do - count = count + 1 - sum = sum + (value - avg)^2 - end - return (sum / count)^.5 -end - --- Comprehensions ("collectors") - --- Shorthand for `for k, v in ... do t[k] = v end` -function iterator.to_table(...) - local t = {} - for k, v in ... do - t[k] = v - end - return t -end - --- Shorthand for `for k in ... do t[#t + 1] = k end` -function iterator.to_list(...) - local t = {} - for k in ... do - t[#t + 1] = k - end - return t -end - --- Shorthand for `for k in ... do t[k] = true end` -function iterator.to_set(...) - local t = {} - for k in ... do - t[k] = true - end - return t -end - -return iterator \ No newline at end of file diff --git a/mods/modlib/json.lua b/mods/modlib/json.lua deleted file mode 100644 index 17950cb2..00000000 --- a/mods/modlib/json.lua +++ /dev/null @@ -1,402 +0,0 @@ -local modlib, setmetatable, pairs, assert, error, table_insert, table_concat, tonumber, tostring, math_huge, string, type, next - = modlib, setmetatable, pairs, assert, error, table.insert, table.concat, tonumber, tostring, math.huge, string, type, next - -local _ENV = {} -setfenv(1, _ENV) - --- See https://tools.ietf.org/id/draft-ietf-json-rfc4627bis-09.html#unichars and https://json.org - --- Null --- TODO consider using userdata (for ex. by using newproxy) -do - local metatable = {} - -- eq is not among the metamethods, len won't work on 5.1 - for _, metamethod in pairs{"add", "sub", "mul", "div", "mod", "pow", "unm", "concat", "len", "lt", "le", "index", "newindex", "call"} do - metatable["__" .. metamethod] = function() return error("attempt to " .. metamethod .. " a null value") end - end - null = setmetatable({}, metatable) -end - -local metatable = {__index = _ENV} -_ENV.metatable = metatable -function new(self) - return setmetatable(self, metatable) -end - -local whitespace = modlib.table.set{"\t", "\r", "\n", " "} -local decoding_escapes = { - ['"'] = '"', - ["\\"] = "\\", - ["/"] = "/", - b = "\b", - f = "\f", - n = "\n", - r = "\r", - t = "\t" - -- TODO is this complete? -} - --- Set up a DFA for number syntax validations -local number_dfa -do -- as a RegEx: (0|(1-9)(0-9)*)[.(0-9)+[(e|E)[+|-](0-9)+]]; does not need to handle the first sign - -- TODO proper DFA utilities - local function set_transitions(state, transitions) - for chars, next_state in pairs(transitions) do - for char in chars:gmatch"." do - state[char] = next_state - end - end - end - local onenine = "123456789" - local digit = "0" .. onenine - local e = "eE" - local exponent = {final = true} - set_transitions(exponent, { - [digit] = exponent - }) - local pre_exponent = {expected = "exponent"} - set_transitions(pre_exponent, { - [digit] = exponent - }) - local exponent_sign = {expected = "exponent"} - set_transitions(exponent_sign, { - [digit] = exponent, - ["+"] = exponent, - ["-"] = exponent - }) - local fraction_final = {final = true} - set_transitions(fraction_final, { - [digit] = fraction_final, - [e] = exponent_sign - }) - local fraction = {expected = "fraction"} - set_transitions(fraction, { - [digit] = fraction_final - }) - local integer = {final = true} - set_transitions(integer, { - [digit] = integer, - [e] = exponent_sign, - ["."] = fraction - }) - local zero = {final = true} - set_transitions(zero, { - ["."] = fraction - }) - number_dfa = {} - set_transitions(number_dfa, { - [onenine] = integer, - ["0"] = zero - }) -end - -local hex_digit_values = {} -for i = 0, 9 do - hex_digit_values[tostring(i)] = i -end -for i = 0, 5 do - hex_digit_values[string.char(("a"):byte() + i)] = 10 + i - hex_digit_values[string.char(("A"):byte() + i)] = 10 + i -end - --- TODO SAX vs DOM -local utf8_char = modlib.utf8.char -function read(self, read_) - local index = 0 - local char - -- TODO support read functions which provide additional debug output (such as row:column) - local function read() - index = index + 1 - char = read_() - return char - end - local function syntax_error(errmsg) - -- TODO ensure the index isn't off - error("syntax error: " .. index .. ": " .. errmsg) - end - local function syntax_assert(value, errmsg) - if not value then - syntax_error(errmsg or "assertion failed!") - end - return value - end - local function skip_whitespace() - while whitespace[char] do - read() - end - end - -- Forward declaration - local value - local function number() - local state = number_dfa - local num = {} - while true do - -- Will work for nil too - local next_state = state[char] - if not next_state then - if not state.final then - if state == number_dfa then - syntax_error"expected a number" - end - syntax_error("invalid number: expected " .. state.expected) - end - return assert(tonumber(table_concat(num))) - end - table_insert(num, char) - state = next_state - read() - end - end - local function utf8_codepoint(codepoint) - return syntax_assert(utf8_char(codepoint), "invalid codepoint") - end - local function string() - local chars = {} - local high_surrogate - while true do - local string_char, next_high_surrogate - if char == '"' then - if high_surrogate then - table_insert(chars, utf8_codepoint(high_surrogate)) - end - return table_concat(chars) - end - if char == "\\" then - read() - if char == "u" then - local codepoint = 0 - for i = 3, 0, -1 do - codepoint = syntax_assert(hex_digit_values[read()], "expected a hex digit") * (16 ^ i) + codepoint - end - if high_surrogate and codepoint >= 0xDC00 and codepoint <= 0xDFFF then - -- TODO strict mode: throw an error for single surrogates - codepoint = 0x10000 + (high_surrogate - 0xD800) * 0x400 + codepoint - 0xDC00 - -- Don't write the high surrogate - high_surrogate = nil - end - if codepoint >= 0xD800 and codepoint <= 0xDBFF then - next_high_surrogate = codepoint - else - string_char = utf8_codepoint(codepoint) - end - else - string_char = syntax_assert(decoding_escapes[char], "invalid escape sequence") - end - else - -- TODO check whether the character is one that must be escaped ("strict" mode) - string_char = syntax_assert(char, "unclosed string") - end - if high_surrogate then - table_insert(chars, utf8_codepoint(high_surrogate)) - end - high_surrogate = next_high_surrogate - if string_char then - table_insert(chars, string_char) - end - read() - end - end - local element - local funcs = { - ['-'] = function() - return -number() - end, - ['"'] = string, - ["{"] = function() - local dict = {} - skip_whitespace() - if char == "}" then return dict end - while true do - syntax_assert(char == '"', "key expected") - read() - local key = string() - read() - skip_whitespace() - syntax_assert(char == ":", "colon expected, got " .. char) - local val = element() - dict[key] = val - if char == "}" then return dict end - syntax_assert(char == ",", "comma expected") - read() - skip_whitespace() - end - end, - ["["] = function() - local list = {} - skip_whitespace() - if char == "]" then return list end - while true do - table_insert(list, value()) - skip_whitespace() - if char == "]" then return list end - syntax_assert(char == ",", "comma expected") - read() - skip_whitespace() - end - end, - } - local function expect_word(word, value) - local msg = word .. " expected" - funcs[word:sub(1, 1)] = function() - syntax_assert(char == word:sub(2, 2), msg) - for i = 3, #word do - read() - syntax_assert(char == word:sub(i, i), msg) - end - return value - end - end - expect_word("true", true) - expect_word("false", false) - expect_word("null", self.null) - function value() - syntax_assert(char, "value expected") - local func = funcs[char] - if func then - -- Advance after first char - read() - local val = func() - -- Advance after last char - read() - return val - end - if char >= "0" and char <= "9" then - return number() - end - syntax_error"value expected" - end - function element() - read() - skip_whitespace() - local val = value() - skip_whitespace() - return val - end - -- TODO consider asserting EOF as read() == nil, perhaps controlled by a parameter - return element() -end - -local encoding_escapes = modlib.table.flip(decoding_escapes) --- Solidus does not need to be escaped -encoding_escapes["/"] = nil --- Control characters. Note: U+0080 to U+009F and U+007F are not considered control characters. -for byte = 0, 0x1F do - encoding_escapes[string.char(byte)] = string.format("u%04X", byte) -end -modlib.table.map(encoding_escapes, function(str) return "\\" .. str end) -local function escape(str) - return str:gsub(".", encoding_escapes) -end -function write(self, value, write) - local null = self.null - local written_strings = self.cache_escaped_strings and setmetatable({}, {__index = function(self, str) - local escaped_str = escape(str) - self[str] = escaped_str - return escaped_str - end}) - local function string(str) - write'"' - write(written_strings and written_strings[str] or escape(str)) - return write'"' - end - local dump - local function write_kv(key, value) - assert(type(key) == "string", "not a dictionary") - string(key) - write":" - dump(value) - end - function dump(value) - if value == null then - -- TODO improve null check (checking for equality doesn't allow using nan as null, for instance) - return write"null" - end - if value == true then - return write"true" - end - if value == false then - return write"false" - end - local type_ = type(value) - if type_ == "number" then - assert(value == value, "unsupported number value: nan") - assert(value ~= math_huge, "unsupported number value: inf") - assert(value ~= -math_huge, "unsupported number value: -inf") - return write(("%.17g"):format(value)) - end - if type_ == "string" then - return string(value) - end - if type_ == "table" then - local table = value - local len = #table - if len == 0 then - local first, value = next(table) - write"{" - if first ~= nil then - write_kv(first, value) - end - for key, value in next, table, first do - write"," - write_kv(key, value) - end - write"}" - else - assert(modlib.table.count(table) == len, "mixed list & hash part") - write"[" - for i = 1, len - 1 do - dump(table[i]) - write"," - end - dump(table[len]) - write"]" - end - return - end - error("unsupported type: " .. type_) - end - dump(value) -end - --- TODO get rid of this paste of write_file and write_string (see modlib.luon) - -function write_file(self, value, file) - return self:write(value, function(text) - file:write(text) - end) -end - -function write_string(self, value) - local rope = {} - self:write(value, function(text) - table_insert(rope, text) - end) - return table_concat(rope) -end - --- TODO read_path (for other serializers too) - -function read_file(self, file) - local value = self:read(function() - return file:read(1) - end) - -- TODO consider file:close() - return value -end - -function read_string(self, string) - -- TODO move the string -> one char read func pattern to modlib.text - local index = 0 - local value = self:read(function() - index = index + 1 - if index > #string then - return - end - return string:sub(index, index) - end) - -- We just expect EOF for strings - assert(index > #string, "EOF expected") - return value -end - -return _ENV \ No newline at end of file diff --git a/mods/modlib/kdtree.lua b/mods/modlib/kdtree.lua deleted file mode 100644 index 5503baef..00000000 --- a/mods/modlib/kdtree.lua +++ /dev/null @@ -1,64 +0,0 @@ --- Localize globals -local assert, math, modlib, setmetatable, table, unpack = assert, math, modlib, setmetatable, table, unpack - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - -local metatable = {__index = _ENV} - -distance = modlib.vector.distance - ---: vectors first vector is used to infer the dimension ---: distance (vector, other_vector) -> number, default: modlib.vector.distance -function new(vectors, distance) - assert(#vectors > 0, "vector list must not be empty") - local dimension = #vectors[1] - local function builder(vectors, axis) - if #vectors == 1 then return { value = vectors[1] } end - table.sort(vectors, function(a, b) return a[axis] > b[axis] end) - local median = math.floor(#vectors / 2) - local next_axis = ((axis + 1) % dimension) + 1 - return setmetatable({ - axis = axis, - pivot = vectors[median], - left = builder({ unpack(vectors, 1, median) }, next_axis), - right = builder({ unpack(vectors, median + 1) }, next_axis) - }, metatable) - end - local self = builder(vectors, 1) - self.distance = distance - return setmetatable(self, metatable) -end - -function get_nearest_neighbor(self, vector) - local min_distance = math.huge - local nearest_neighbor - local distance_func = self.distance - local function visit(tree) - local axis = tree.axis - if tree.value ~= nil then - local distance = distance_func(tree.value, vector) - if distance < min_distance then - min_distance = distance - nearest_neighbor = tree.value - end - return - else - local this_side, other_side = tree.left, tree.right - if vector[axis] < tree.pivot[axis] then this_side, other_side = other_side, this_side end - visit(this_side) - if tree.pivot then - local dist = math.abs(tree.pivot[axis] - vector[axis]) - if dist <= min_distance then visit(other_side) end - end - end - end - visit(self) - return nearest_neighbor, min_distance -end - --- TODO insertion & deletion + rebalancing - --- Export environment -return _ENV \ No newline at end of file diff --git a/mods/modlib/less_than.lua b/mods/modlib/less_than.lua deleted file mode 100644 index 81d7dc6a..00000000 --- a/mods/modlib/less_than.lua +++ /dev/null @@ -1,53 +0,0 @@ --- Comparator utilities for "less than" functions returning whether a < b -local less_than = {} -setfenv(1, less_than) - -default = {} - -function default.less_than(a, b) return a < b end; default.lt = default.less_than -function default.less_or_equal(a, b) return a <= b end; default.leq = default.less_or_equal -function default.greater_than(a, b) return a > b end; default.gt = default.greater_than -function default.greater_or_equal(a, b) return a >= b end; default.geq = default.greater_or_equal - -function less_or_equal(less_than) - return function(a, b) return not less_than(b, a) end -end -leq = less_or_equal - -function greater_or_equal(less_than) - return function(a, b) return not less_than(a, b) end -end -geq = greater_or_equal - -function greater_than(less_than) - return function(a, b) return less_than(b, a) end -end -gt = greater_than - -function equal(less_than) - return function(a, b) - return not (less_than(a, b) or less_than(b, a)) - end -end - -function relation(less_than) - return function(a, b) - if less_than(a, b) then return "<" - elseif less_than(b, a) then return ">" - else return "=" end - end -end - -function by_func(func) - return function(a, b) - return func(a) < func(b) - end -end - -function by_field(key) - return function(a, b) - return a[key] < b[key] - end -end - -return less_than \ No newline at end of file diff --git a/mods/modlib/logo.svg b/mods/modlib/logo.svg deleted file mode 100644 index e129bbc0..00000000 --- a/mods/modlib/logo.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - image/svg+xml - - - - - - - - - diff --git a/mods/modlib/luon.lua b/mods/modlib/luon.lua deleted file mode 100644 index c9df4ba0..00000000 --- a/mods/modlib/luon.lua +++ /dev/null @@ -1,207 +0,0 @@ --- Lua module to serialize values as Lua code - -local assert, error, rawget, pairs, pcall, type, setfenv, setmetatable, select, loadstring, loadfile - = assert, error, rawget, pairs, pcall, type, setfenv, setmetatable, select, loadstring, loadfile - -local table_concat, string_format, math_huge - = table.concat, string.format, math.huge - -local count_objects = modlib.table.count_objects -local is_identifier = modlib.text.is_identifier - -local function quote(string) - return string_format("%q", string) -end - -local _ENV = {} -setfenv(1, _ENV) -local metatable = {__index = _ENV} -_ENV.metatable = metatable - -function new(self) - return setmetatable(self, metatable) -end - -function aux_write(_self, _object) - -- returns reader, arguments - return -end - -aux_read = {} - -function write(self, value, write) - -- TODO evaluate custom aux. writers *before* writing for circular structs - local reference, refnum = "1", 1 - -- [object] = reference - local references = {} - -- Circular tables that must be filled using `table[key] = value` statements - local to_fill = {} - - -- TODO (?) sort objects by count, give frequently referenced objects shorter references - for object, count in pairs(count_objects(value)) do - local type_ = type(object) - -- Object must appear more than once. If it is a string, the reference has to be shorter than the string. - if count >= 2 and (type_ ~= "string" or #reference + 5 < #object) then - if refnum == 1 then - write"local _={};" -- initialize reference table - end - write"_[" - write(reference) - write"]=" - if type_ == "table" then - write"{}" - elseif type_ == "string" then - write(quote(object)) - end - write";" - references[object] = reference - if type_ == "table" then - to_fill[object] = reference - end - refnum = refnum + 1 - reference = string_format("%d", refnum) - end - end - -- Used to decide whether we should do "key=..." - local function use_short_key(key) - return not references[key] and type(key) == "string" and is_identifier(key) - end - local function dump(value) - -- Primitive types - if value == nil then - return write"nil" - end if value == true then - return write"true" - end if value == false then - return write"false" - end - local type_ = type(value) - if type_ == "number" then - -- Explicit handling of special values for forwards compatibility - if value ~= value then -- nan - return write"0/0" - end if value == math_huge then - return write"1/0" - end if value == -math_huge then - return write"-1/0" - end - return write(string_format("%.17g", value)) - end - -- Reference types: table and string - local ref = references[value] - if ref then - -- Referenced - write"_[" - write(ref) - return write"]" - end if type_ == "string" then - return write(quote(value)) - end if type_ == "table" then - write"{" - -- First write list keys: - -- Don't use the table length #value here as it may horribly fail - -- for tables which use large integers as keys in the hash part; - -- stop at the first "hole" (nil value) instead - local len = 0 - local first = true -- whether this is the first entry, which may not have a leading comma - while true do - local v = rawget(value, len + 1) -- use rawget to avoid metatables like the vector metatable - if v == nil then break end - if first then first = false else write(",") end - dump(v) - len = len + 1 - end - -- Now write map keys ([key] = value) - for k, v in pairs(value) do - -- We have written all non-float keys in [1, len] already - if type(k) ~= "number" or k % 1 ~= 0 or k < 1 or k > len then - if first then first = false else write(",") end - if use_short_key(k) then - write(k) - else - write"[" - dump(k) - write"]" - end - write"=" - dump(v) - end - end - return write"}" - end - -- TODO move aux_write to start, to allow dealing with metatables etc.? - return (function(func, ...) - -- functions are the only way to deal with varargs - if not func then - return error("unsupported type: " .. type_) - end - write(func) - write"(" - local n = select("#", ...) - for i = 1, n - 1 do - dump(select(i, ...)) - write"," - end - if n > 0 then - dump(select(n, ...)) - end - write")" - end)(self:aux_write(value)) - end - -- Write the statements to fill circular tables - for table, ref in pairs(to_fill) do - for k, v in pairs(table) do - write"_[" - write(ref) - write"]" - if use_short_key(k) then - write"." - write(k) - else - write"[" - dump(k) - write"]" - end - write"=" - dump(v) - write";" - end - end - write"return " - dump(value) -end - -function write_file(self, value, file) - return self:write(value, function(text) - file:write(text) - end) -end - -function write_string(self, value) - local rope = {} - self:write(value, function(text) - rope[#rope + 1] = text - end) - return table_concat(rope) -end - -function read(self, ...) - local read = assert(...) - -- math.huge was serialized to inf, 0/0 was serialized to -nan by `%.17g` - setfenv(read, setmetatable({inf = math_huge, nan = 0/0}, {__index = self.aux_read})) - local success, value_or_err = pcall(read) - if success then - return value_or_err - end - return nil, value_or_err -end - -function read_file(self, path) - return self:read(loadfile(path)) -end - -function read_string(self, string) - return self:read(loadstring(string)) -end - -return _ENV \ No newline at end of file diff --git a/mods/modlib/math.lua b/mods/modlib/math.lua deleted file mode 100644 index d7f01dfb..00000000 --- a/mods/modlib/math.lua +++ /dev/null @@ -1,182 +0,0 @@ --- Localize globals -local assert, math, math_floor, minetest, modlib_table_reverse, os, string_char, select, setmetatable, table_insert, table_concat - = assert, math, math.floor, minetest, modlib.table.reverse, os, string.char, select, setmetatable, table.insert, table.concat - -local inf = math.huge - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - --- TODO might be too invasive --- Make random random -math.randomseed(minetest and minetest.get_us_time() or os.time() + os.clock()) -for _ = 1, 100 do math.random() end - -negative_nan = 0/0 -positive_nan = negative_nan ^ 1 - -function sign(number) - if number ~= number then return number end -- nan - if number == 0 then return 0 end - if number < 0 then return -1 end - if number > 0 then return 1 end -end - -function clamp(number, min, max) - return math.min(math.max(number, min), max) -end - --- Random integer from 0 to 2^53 - 1 (inclusive) -local function _randint() - return math.random(0, 2^27 - 1) * 2^26 + math.random(0, 2^26 - 1) -end - --- Random float from 0 to 1 (exclusive) -local function _randfloat() - return _randint() / (2^53) -end - ---+ Increased randomness float random without overflows ---+ `random()`: Random number from `0` to `1` (exclusive) ---+ `random(max)`: Random number from `0` to `max` (exclusive) ---+ `random(min, max)`: Random number from `min` to `max` (exclusive) -function random(...) - local n = select("#", ...) - if n == 0 then - return _randfloat() - end if n == 1 then - local max = ... - return _randfloat() * max - end do assert(n == 2) - local min, max = ... - return min + (max - min) * _randfloat() - end -end - --- Increased randomness integer random ---+ `randint()`: Random integer from `0` to `2^53 - 1` (inclusive) ---+ `randint(max)`: Random integer from `0` to `max` (inclusive) ---+ `randint(min, max)`: Random integer from `min` to `max` (inclusive) -function randint(...) - local n = select("#", ...) - if n == 0 then - return _randint() - end if n == 1 then - local max = ... - return math.floor(_randfloat() * max + 0.5) - end do assert(n == 2) - local min, max = ... - return min + math.floor(_randfloat() * (max - min) + 0.5) - end -end - -log = setmetatable({}, { - __index = function(self, base) - local div = math.log(base) - local function base_log(number) - return math.log(number) / div - end - self[base] = base_log - return base_log - end, - __call = function(_, number, base) - if not base then - return math.log(number) - end - return math.log(number) / math.log(base) - end -}) - --- one-based mod -function onemod(number, modulus) - return ((number - 1) % modulus) + 1 -end - -function round(number, steps) - steps = steps or 1 - return math_floor(number * steps + 0.5) / steps -end - -local c0 = ("0"):byte() -local cA = ("A"):byte() - -function default_digit_function(digit) - if digit <= 9 then return string_char(c0 + digit) end - return string_char(cA + digit - 10) -end - -default_precision = 10 - --- See https://github.com/appgurueu/Luon/blob/master/index.js#L724 -function tostring(number, base, digit_function, precision) - if number ~= number then - return "nan" - end - if number == inf then - return "inf" - end - if number == -inf then - return "-inf" - end - digit_function = digit_function or default_digit_function - precision = precision or default_precision - local out = {} - if number < 0 then - table_insert(out, "-") - number = -number - end - -- Rounding - number = number + base ^ -precision / 2 - local digit - while number >= base do - digit = math_floor(number % base) - table_insert(out, digit_function(digit)) - number = (number - digit) / base - end - digit = math_floor(number) - table_insert(out, digit_function(digit)) - modlib_table_reverse(out) - number = number % 1 - if number ~= 0 and number >= base ^ -precision then - table_insert(out, ".") - while precision >= 0 and number >= base ^ -precision do - number = number * base - digit = math_floor(number % base) - table_insert(out, digit_function(digit)) - number = number - digit - precision = precision - 1 - end - end - return table_concat(out) -end - --- See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround#polyfill --- Rounds a 64-bit float to a 32-bit float; --- if the closest 32-bit float is out of bounds, --- the appropriate infinity is returned. -function fround(number) - if number == 0 or number ~= number then - return number - end - local sign = 1 - if number < 0 then - sign = -1 - number = -number - end - local _, exp = math.frexp(number) - exp = exp - 1 -- we want 2^exponent >= number > 2^(exponent-1) - local powexp = 2 ^ math.max(-126, math.min(exp, 127)) - local leading = exp <= -127 and 0 or 1 -- subnormal number? - local mantissa = math.floor((number / powexp - leading) * 0x800000 + 0.5) - if - mantissa > 0x800000 -- doesn't fit in mantissa - or (exp >= 127 and mantissa == 0x800000) -- fits if the exponent can be increased - then - return sign * inf - end - return sign * powexp * (leading + mantissa / 0x800000) -end - --- Export environment -return _ENV diff --git a/mods/modlib/matrix4.lua b/mods/modlib/matrix4.lua deleted file mode 100644 index 4303717e..00000000 --- a/mods/modlib/matrix4.lua +++ /dev/null @@ -1,180 +0,0 @@ --- Simple 4x4 matrix for 3d transformations (translation, rotation, scale); --- provides exactly the methods needed to calculate inverse bind matrices (for b3d -> glTF conversion) -local mat4 = {} -local metatable = {__index = mat4} - -function mat4.new(rows) - assert(#rows == 4) - for i = 1, 4 do - assert(#rows[i] == 4) - end - return setmetatable(rows, metatable) -end - -function mat4.identity() - return mat4.new{ - {1, 0, 0, 0}, - {0, 1, 0, 0}, - {0, 0, 1, 0}, - {0, 0, 0, 1}, - } -end - --- Matrices can't properly represent translation: --- => work with 4d vectors, assume w = 1. -function mat4.translation(vec) - assert(#vec == 3) - local x, y, z = unpack(vec) - return mat4.new{ - {1, 0, 0, x}, - {0, 1, 0, y}, - {0, 0, 1, z}, - {0, 0, 0, 1}, - } -end - --- See https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation -function mat4.rotation(unit_quat) - assert(#unit_quat == 4) - local x, y, z, w = unpack(unit_quat) -- TODO (?) assert unit quaternion - return mat4.new{ - {1 - 2*(y^2 + z^2), 2*(x*y - z*w), 2*(x*z + y*w), 0}, - {2*(x*y + z*w), 1 - 2*(x^2 + z^2), 2*(y*z - x*w), 0}, - {2*(x*z - y*w), 2*(y*z + x*w), 1 - 2*(x^2 + y^2), 0}, - {0, 0, 0, 1}, - } -end - -function mat4.scale(vec) - assert(#vec == 3) - local x, y, z = unpack(vec) - return mat4.new{ - {x, 0, 0, 0}, - {0, y, 0, 0}, - {0, 0, z, 0}, - {0, 0, 0, 1}, - } -end - --- Apply `self` to a 4d modlib vector `vec` -function mat4:apply(vec) - assert(#vec == 4) - local res = {} - for i = 1, 4 do - local sum = 0 - for j = 1, 4 do - sum = sum + self[i][j] * vec[j] - end - res[i] = sum - end - return vec.new(res) -end - --- Multiplication: First apply other, then self ---> Matrix product `self * other` -function mat4:multiply(other) - local res = {} - for i = 1, 4 do - res[i] = {} - for j = 1, 4 do - local sum = 0 -- dot product of row & col vec - for k = 1, 4 do - sum = sum + self[i][k] * other[k][j] - end - res[i][j] = sum - end - end - return mat4.new(res) -end - --- Composition: First apply self, then other -function mat4:compose(other) - return other:multiply(self) -- equivalent to `other * self` in terms of matrix multiplication -end - --- Matrix inversion using Gauss-Jordan elimination -do - -- Fundamental operations - local function _swap_rows(mat, i, j) - mat[i], mat[j] = mat[j], mat[i] - end - local function _scale_row(mat, factor, row_idx) - for i = 1, 4 do - mat[row_idx][i] = factor * mat[row_idx][i] - end - end - local function _add_row_with_factor(mat, factor, src_row_idx, dst_row_idx) - assert(src_row_idx ~= dst_row_idx) - for i = 1, 4 do - mat[dst_row_idx][i] = mat[dst_row_idx][i] + factor * mat[src_row_idx][i] - end - end - - local epsilon = 1e-6 -- small threshold; values below this are considered zero - function mat4:inverse() - local inv = mat4.identity() -- inverse matrix: all elimination operations will also be applied to this - local copy = {} -- copy of `self` the Gaussian elimination is being executed on - for i = 1, 4 do - copy[i] = {} - for j = 1, 4 do - copy[i][j] = self[i][j] - end - end - - -- All operations must be mirrored to the inverse matrix - local function swap_rows(i, j) - _swap_rows(copy, i, j) - _swap_rows(inv, i, j) - end - local function scale_row(factor, row_idx) - _scale_row(copy, factor, row_idx) - _scale_row(inv, factor, row_idx) - end - local function add_with_factor(factor, src_row_idx, dst_row_idx) - _add_row_with_factor(copy, factor, src_row_idx, dst_row_idx) - _add_row_with_factor(inv, factor, src_row_idx, dst_row_idx) - end - - -- Elimination phase - for col_idx = 1, 4 do - -- Find a pivot row: Choose the row with the largest absolute component - local max_row_idx = col_idx - local max_abs_comp = math.abs(copy[max_row_idx][col_idx]) - for row_idx = col_idx, 4 do - local cand_comp = math.abs(copy[row_idx][col_idx]) - if cand_comp > max_abs_comp then - max_row_idx, max_abs_comp = row_idx, cand_comp - end - end - - -- Assert that there is a row that has this component "nonzero" - assert(max_abs_comp >= epsilon, "matrix not invertible!") - - swap_rows(col_idx, max_row_idx) -- swap row to correct position - -- Eliminate the `col_idx`-th component in all rows *below* the pivot row - local pivot_value = copy[col_idx][col_idx] - for row_idx = col_idx + 1, 4 do - local factor = -copy[row_idx][col_idx] / pivot_value - add_with_factor(factor, col_idx, row_idx) - assert(math.abs(copy[row_idx][col_idx]) < epsilon) -- should be eliminated now - end - end - - -- Resubstitution phase - pretty much the same but in reverse and without swapping - for col_idx = 4, 1, -1 do - local pivot_value = copy[col_idx][col_idx] - -- Eliminate the `col_idx`-th component in all rows *above* the pivot row - for row_idx = col_idx - 1, 1, -1 do - local factor = -copy[row_idx][col_idx] / pivot_value - add_with_factor(factor, col_idx, row_idx) - assert(math.abs(copy[row_idx][col_idx]) < epsilon) -- should be eliminated now - end - scale_row(1/pivot_value, col_idx) -- normalize row - end - - -- Done: `copy` should now be the identity matrix <=> `inv` is the inverse. - return inv - end -end - -return mat4 diff --git a/mods/modlib/minetest.lua b/mods/modlib/minetest.lua deleted file mode 100644 index 629e442b..00000000 --- a/mods/modlib/minetest.lua +++ /dev/null @@ -1,90 +0,0 @@ -local _ENV = {} - -local components = {} -for _, value in pairs{ - "mod", - "luon", - "raycast", - "schematic", - "colorspec", - "media", - "obj", - "texmod", -} do - components[value] = value -end - --- These dirty files have to write to the modlib.minetest environment -local dirty_files = {} -for filename, comps in pairs{ - -- get_gametime is missing from here as it is forceloaded in init.lua - misc = { - "max_wear", - "override", - "after", - "register_globalstep", - "form_listeners", - "register_form_listener", - "texture_modifier_inventorycube", - "get_node_inventory_image", - "check_player_privs", - "decode_base64", - "objects_inside_radius", - "objects_inside_area", - "nodename_matcher", - "playerdata", - "connected_players", - "set_privs", - "register_on_leaveplayer", - "get_mod_info", - "get_mod_load_order" - }, - liquid = { - "liquid_level_max", - "get_liquid_corner_levels", - "flowing_downwards", - "get_liquid_flow_direction" - }, - wielditem_change = { - "players", - "registered_on_wielditem_changes", - "register_on_wielditem_change" - }, - colorspec = { - "named_colors", - "colorspec_to_colorstring" - }, - boxes = { - "get_node_boxes", - "get_node_collisionboxes", - "get_node_selectionboxes", - }, - png = { - "decode_png", - "convert_png_to_argb8", - "encode_png", - } -} do - for _, component in pairs(comps) do - components[component] = filename - end - dirty_files[filename] = true -end - -local modpath, concat_path = minetest.get_modpath(modlib.modname), modlib.file.concat_path - -setmetatable(_ENV, {__index = function(_ENV, name) - local filename = components[name] - if filename then - local loader = assert(loadfile(concat_path{modpath, "minetest", filename .. ".lua"})) - if dirty_files[filename] then - loader(_ENV) - return rawget(_ENV, name) - end - local module = loader() - _ENV[name] = module - return module - end -end}) - -return _ENV \ No newline at end of file diff --git a/mods/modlib/minetest/boxes.lua b/mods/modlib/minetest/boxes.lua deleted file mode 100644 index 4c193a6f..00000000 --- a/mods/modlib/minetest/boxes.lua +++ /dev/null @@ -1,166 +0,0 @@ --- Localize globals -local assert, ipairs, math, minetest, table, type, vector - = assert, ipairs, math, minetest, table, type, vector - --- Set environment -local _ENV = ... -setfenv(1, _ENV) - --- Minetest allows shorthand box = {...} instead of {{...}} -local function get_boxes(box_or_boxes) - return type(box_or_boxes[1]) == "number" and {box_or_boxes} or box_or_boxes -end - -local has_boxes_prop = {collision_box = "walkable", selection_box = "pointable"} - --- Required for raycast box IDs to be accurate -local connect_sides_order = {"top", "bottom", "front", "left", "back", "right"} - -local connect_sides_directions = { - top = vector.new(0, 1, 0), - bottom = vector.new(0, -1, 0), - front = vector.new(0, 0, -1), - left = vector.new(-1, 0, 0), - back = vector.new(0, 0, 1), - right = vector.new(1, 0, 0), -} - ---> list of collisionboxes in Minetest format -local function get_node_boxes(pos, type) - local node = minetest.get_node(pos) - local node_def = minetest.registered_nodes[node.name] - if not node_def or node_def[has_boxes_prop[type]] == false then - return {} - end - local boxes = {{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}} - local def_node_box = node_def.drawtype == "nodebox" and node_def.node_box - local def_box = node_def[type] or def_node_box -- will evaluate to def_node_box for type = nil - if not def_box then - return boxes -- default to regular box - end - local box_type = def_box.type - if box_type == "regular" then - return boxes - end - local fixed = def_box.fixed - boxes = get_boxes(fixed or {}) - local paramtype2 = node_def.paramtype2 - if box_type == "leveled" then - boxes = table.copy(boxes) - local level = (paramtype2 == "leveled" and node.param2 or node_def.leveled or 0) / 255 - 0.5 - for _, box in ipairs(boxes) do - box[5] = level - end - elseif box_type == "wallmounted" then - local dir = minetest.wallmounted_to_dir((paramtype2 == "colorwallmounted" and node.param2 % 8 or node.param2) or 0) - local box - -- The (undocumented!) node box defaults below are taken from `NodeBox::reset` - if dir.y > 0 then - box = def_box.wall_top or {-0.5, 0.5 - 1/16, -0.5, 0.5, 0.5, 0.5} - elseif dir.y < 0 then - box = def_box.wall_bottom or {-0.5, -0.5, -0.5, 0.5, -0.5 + 1/16, 0.5} - else - box = def_box.wall_side or {-0.5, -0.5, -0.5, -0.5 + 1/16, 0.5, 0.5} - if dir.z > 0 then - box = {box[3], box[2], -box[4], box[6], box[5], -box[1]} - elseif dir.z < 0 then - box = {-box[6], box[2], box[1], -box[3], box[5], box[4]} - elseif dir.x > 0 then - box = {-box[4], box[2], box[3], -box[1], box[5], box[6]} - else - box = {box[1], box[2], -box[6], box[4], box[5], -box[3]} - end - end - return {assert(box, "incomplete wallmounted collisionbox definition of " .. node.name)} - end - if box_type == "connected" then - boxes = table.copy(boxes) - local connect_sides = connect_sides_directions -- (ab)use directions as a "set" of sides - if node_def.connect_sides then -- build set of sides from given list - connect_sides = {} - for _, side in ipairs(node_def.connect_sides) do - connect_sides[side] = true - end - end - local function add_collisionbox(key) - for _, box in ipairs(get_boxes(def_box[key] or {})) do - table.insert(boxes, box) - end - end - local matchers = {} - for i, nodename_or_group in ipairs(node_def.connects_to or {}) do - matchers[i] = nodename_matcher(nodename_or_group) - end - local function connects_to(nodename) - for _, matcher in ipairs(matchers) do - if matcher(nodename) then - return true - end - end - end - local connected, connected_sides - for _, side in ipairs(connect_sides_order) do - if connect_sides[side] then - local direction = connect_sides_directions[side] - local neighbor = minetest.get_node(vector.add(pos, direction)) - local connects = connects_to(neighbor.name) - connected = connected or connects - connected_sides = connected_sides or (side ~= "top" and side ~= "bottom") - add_collisionbox((connects and "connect_" or "disconnected_") .. side) - end - end - if not connected then - add_collisionbox("disconnected") - end - if not connected_sides then - add_collisionbox("disconnected_sides") - end - return boxes - end - if box_type == "fixed" and paramtype2 == "facedir" or paramtype2 == "colorfacedir" then - local param2 = paramtype2 == "colorfacedir" and node.param2 % 32 or node.param2 or 0 - if param2 ~= 0 then - boxes = table.copy(boxes) - local axis = ({5, 6, 3, 4, 1, 2})[math.floor(param2 / 4) + 1] - local other_axis_1, other_axis_2 = (axis % 3) + 1, ((axis + 1) % 3) + 1 - local rotation = (param2 % 4) / 2 * math.pi - local flip = axis > 3 - if flip then axis = axis - 3; rotation = -rotation end - local sin, cos = math.sin(rotation), math.cos(rotation) - if axis == 2 then - sin = -sin - end - for _, box in ipairs(boxes) do - for off = 0, 3, 3 do - local axis_1, axis_2 = other_axis_1 + off, other_axis_2 + off - local value_1, value_2 = box[axis_1], box[axis_2] - box[axis_1] = value_1 * cos - value_2 * sin - box[axis_2] = value_1 * sin + value_2 * cos - end - if not flip then - box[axis], box[axis + 3] = -box[axis + 3], -box[axis] - end - local function fix(coord) - if box[coord] > box[coord + 3] then - box[coord], box[coord + 3] = box[coord + 3], box[coord] - end - end - fix(other_axis_1) - fix(other_axis_2) - end - end - end - return boxes -end - -function _ENV.get_node_boxes(pos) - return get_node_boxes(pos, nil) -end - -function get_node_selectionboxes(pos) - return get_node_boxes(pos, "selection_box") -end - -function get_node_collisionboxes(pos) - return get_node_boxes(pos, "collision_box") -end diff --git a/mods/modlib/minetest/colorspec.lua b/mods/modlib/minetest/colorspec.lua deleted file mode 100644 index 59798df8..00000000 --- a/mods/modlib/minetest/colorspec.lua +++ /dev/null @@ -1,325 +0,0 @@ --- Localize globals -local assert, error, math, minetest, setmetatable, tonumber, type = assert, error, math, minetest, setmetatable, tonumber, type -local floor = math.floor - --- Set environment -local _ENV = ... -setfenv(1, _ENV) - --- As in src/util/string.cpp -named_colors = { - aliceblue = 0xf0f8ff, - antiquewhite = 0xfaebd7, - aqua = 0x00ffff, - aquamarine = 0x7fffd4, - azure = 0xf0ffff, - beige = 0xf5f5dc, - bisque = 0xffe4c4, - black = 0x000000, - blanchedalmond = 0xffebcd, - blue = 0x0000ff, - blueviolet = 0x8a2be2, - brown = 0xa52a2a, - burlywood = 0xdeb887, - cadetblue = 0x5f9ea0, - chartreuse = 0x7fff00, - chocolate = 0xd2691e, - coral = 0xff7f50, - cornflowerblue = 0x6495ed, - cornsilk = 0xfff8dc, - crimson = 0xdc143c, - cyan = 0x00ffff, - darkblue = 0x00008b, - darkcyan = 0x008b8b, - darkgoldenrod = 0xb8860b, - darkgray = 0xa9a9a9, - darkgreen = 0x006400, - darkgrey = 0xa9a9a9, - darkkhaki = 0xbdb76b, - darkmagenta = 0x8b008b, - darkolivegreen = 0x556b2f, - darkorange = 0xff8c00, - darkorchid = 0x9932cc, - darkred = 0x8b0000, - darksalmon = 0xe9967a, - darkseagreen = 0x8fbc8f, - darkslateblue = 0x483d8b, - darkslategray = 0x2f4f4f, - darkslategrey = 0x2f4f4f, - darkturquoise = 0x00ced1, - darkviolet = 0x9400d3, - deeppink = 0xff1493, - deepskyblue = 0x00bfff, - dimgray = 0x696969, - dimgrey = 0x696969, - dodgerblue = 0x1e90ff, - firebrick = 0xb22222, - floralwhite = 0xfffaf0, - forestgreen = 0x228b22, - fuchsia = 0xff00ff, - gainsboro = 0xdcdcdc, - ghostwhite = 0xf8f8ff, - gold = 0xffd700, - goldenrod = 0xdaa520, - gray = 0x808080, - green = 0x008000, - greenyellow = 0xadff2f, - grey = 0x808080, - honeydew = 0xf0fff0, - hotpink = 0xff69b4, - indianred = 0xcd5c5c, - indigo = 0x4b0082, - ivory = 0xfffff0, - khaki = 0xf0e68c, - lavender = 0xe6e6fa, - lavenderblush = 0xfff0f5, - lawngreen = 0x7cfc00, - lemonchiffon = 0xfffacd, - lightblue = 0xadd8e6, - lightcoral = 0xf08080, - lightcyan = 0xe0ffff, - lightgoldenrodyellow = 0xfafad2, - lightgray = 0xd3d3d3, - lightgreen = 0x90ee90, - lightgrey = 0xd3d3d3, - lightpink = 0xffb6c1, - lightsalmon = 0xffa07a, - lightseagreen = 0x20b2aa, - lightskyblue = 0x87cefa, - lightslategray = 0x778899, - lightslategrey = 0x778899, - lightsteelblue = 0xb0c4de, - lightyellow = 0xffffe0, - lime = 0x00ff00, - limegreen = 0x32cd32, - linen = 0xfaf0e6, - magenta = 0xff00ff, - maroon = 0x800000, - mediumaquamarine = 0x66cdaa, - mediumblue = 0x0000cd, - mediumorchid = 0xba55d3, - mediumpurple = 0x9370db, - mediumseagreen = 0x3cb371, - mediumslateblue = 0x7b68ee, - mediumspringgreen = 0x00fa9a, - mediumturquoise = 0x48d1cc, - mediumvioletred = 0xc71585, - midnightblue = 0x191970, - mintcream = 0xf5fffa, - mistyrose = 0xffe4e1, - moccasin = 0xffe4b5, - navajowhite = 0xffdead, - navy = 0x000080, - oldlace = 0xfdf5e6, - olive = 0x808000, - olivedrab = 0x6b8e23, - orange = 0xffa500, - orangered = 0xff4500, - orchid = 0xda70d6, - palegoldenrod = 0xeee8aa, - palegreen = 0x98fb98, - paleturquoise = 0xafeeee, - palevioletred = 0xdb7093, - papayawhip = 0xffefd5, - peachpuff = 0xffdab9, - peru = 0xcd853f, - pink = 0xffc0cb, - plum = 0xdda0dd, - powderblue = 0xb0e0e6, - purple = 0x800080, - rebeccapurple = 0x663399, - red = 0xff0000, - rosybrown = 0xbc8f8f, - royalblue = 0x4169e1, - saddlebrown = 0x8b4513, - salmon = 0xfa8072, - sandybrown = 0xf4a460, - seagreen = 0x2e8b57, - seashell = 0xfff5ee, - sienna = 0xa0522d, - silver = 0xc0c0c0, - skyblue = 0x87ceeb, - slateblue = 0x6a5acd, - slategray = 0x708090, - slategrey = 0x708090, - snow = 0xfffafa, - springgreen = 0x00ff7f, - steelblue = 0x4682b4, - tan = 0xd2b48c, - teal = 0x008080, - thistle = 0xd8bfd8, - tomato = 0xff6347, - turquoise = 0x40e0d0, - violet = 0xee82ee, - wheat = 0xf5deb3, - white = 0xffffff, - whitesmoke = 0xf5f5f5, - yellow = 0xffff00, - yellowgreen = 0x9acd32 -} - -colorspec = {} - -local metatable = {__index = colorspec} -colorspec.metatable = metatable - -function colorspec.new(table) - return setmetatable({ - r = assert(table.r), - g = assert(table.g), - b = assert(table.b), - a = table.a or 255 - }, metatable) -end - -colorspec.from_table = colorspec.new - -local c_comp = { "r", "g", "g", "b", "b", "r" } -local x_comp = { "g", "r", "b", "g", "r", "b" } -function colorspec.from_hsv( - -- 0 (inclusive) to 1 (exclusive) - hue, - -- 0 to 1 (both inclusive) - saturation, - -- 0 to 1 (both inclusive) - value -) - hue = hue * 6 - local chroma = saturation * value - local m = value - chroma - local color = {r = m, g = m, b = m} - local idx = 1 + floor(hue) - color[c_comp[idx]] = color[c_comp[idx]] + chroma - local x = chroma * (1 - math.abs(hue % 2 - 1)) - color[x_comp[idx]] = color[x_comp[idx]] + x - color.r = floor(color.r * 255 + 0.5) - color.g = floor(color.g * 255 + 0.5) - color.b = floor(color.b * 255 + 0.5) - return colorspec.from_table(color) -end - -function colorspec.from_string(string) - string = string:lower() -- names and hex are case-insensitive - local number, alpha = named_colors[string], 0xFF - if not number then - local name, alpha_text = string:match("^([a-z]+)#(%x+)$") - if name then - if alpha_text:len() > 2 then - return - end - number = named_colors[name] - if not number then - return - end - alpha = tonumber(alpha_text, 0x10) - if alpha_text:len() == 1 then - alpha = alpha * 0x11 - end - end - end - if number then - return colorspec.from_number_rgba(number * 0x100 + alpha) - end - local hex_text = string:match("^#(%x+)$") - if not hex_text then - return - end - local len, num = hex_text:len(), tonumber(hex_text, 0x10) - if len == 8 then - return colorspec.from_number_rgba(num) - end - if len == 6 then - return colorspec.from_number_rgba(num * 0x100 + 0xFF) - end - if len == 4 then - return colorspec.from_table{ - a = (num % 0x10) * 0x11, - b = (floor(num / 0x10) % 0x10) * 0x11, - g = (floor(num / (0x100)) % 0x10) * 0x11, - r = (floor(num / (0x1000)) % 0x10) * 0x11 - } - end - if len == 3 then - return colorspec.from_table{ - b = (num % 0x10) * 0x11, - g = (floor(num / 0x10) % 0x10) * 0x11, - r = (floor(num / (0x100)) % 0x10) * 0x11 - } - end -end - -colorspec.from_text = colorspec.from_string - -function colorspec.from_number_rgba(number) - return colorspec.from_table{ - a = number % 0x100, - b = floor(number / 0x100) % 0x100, - g = floor(number / 0x10000) % 0x100, - r = floor(number / 0x1000000) - } -end - -function colorspec.from_number_rgb(number) - return colorspec.from_table{ - a = 0xFF, - b = number % 0x100, - g = floor(number / 0x100) % 0x100, - r = floor(number / 0x10000) - } -end - -function colorspec.from_number(number) - return colorspec.from_table{ - b = number % 0x100, - g = floor(number / 0x100) % 0x100, - r = floor(number / 0x10000) % 0x100, - a = floor(number / 0x1000000) - } -end - -function colorspec.from_any(value) - local type = type(value) - if type == "table" then - return colorspec.from_table(value) - end - if type == "string" then - return colorspec.from_string(value) - end - if type == "number" then - return colorspec.from_number(value) - end - error("Unsupported type " .. type) -end - -function colorspec:to_table() - return self -end - ---> hex string, omits alpha if possible (if opaque) -function colorspec:to_string() - if self.a == 255 then - return ("#%02X%02X%02X"):format(self.r, self.g, self.b) - end - return ("#%02X%02X%02X%02X"):format(self.r, self.g, self.b, self.a) -end -metatable.__tostring = colorspec.to_string - -function colorspec:to_number_rgba() - return self.r * 0x1000000 + self.g * 0x10000 + self.b * 0x100 + self.a -end - -function colorspec:to_number_rgb() - return self.r * 0x10000 + self.g * 0x100 + self.b -end - -function colorspec:to_number() - return self.a * 0x1000000 + self.r * 0x10000 + self.g * 0x100 + self.b -end - -colorspec_to_colorstring = minetest.colorspec_to_colorstring or function(spec) - local color = colorspec.from_any(spec) - if not color then - return nil - end - return color:to_string() -end diff --git a/mods/modlib/minetest/gametime.lua b/mods/modlib/minetest/gametime.lua deleted file mode 100644 index ebe44aa8..00000000 --- a/mods/modlib/minetest/gametime.lua +++ /dev/null @@ -1,16 +0,0 @@ -local gametime -minetest.register_globalstep(function(dtime) - if gametime then - gametime = gametime + dtime - return - end - gametime = assert(minetest.get_gametime()) - function modlib.minetest.get_gametime() - local imprecise_gametime = minetest.get_gametime() - if imprecise_gametime > gametime then - minetest.log("warning", "modlib.minetest.get_gametime(): Called after increment and before first globalstep") - return imprecise_gametime - end - return gametime - end -end) \ No newline at end of file diff --git a/mods/modlib/minetest/liquid.lua b/mods/modlib/minetest/liquid.lua deleted file mode 100644 index 3e956f72..00000000 --- a/mods/modlib/minetest/liquid.lua +++ /dev/null @@ -1,122 +0,0 @@ --- Localize globals -local math, minetest, modlib, pairs = math, minetest, modlib, pairs - --- Set environment -local _ENV = ... -setfenv(1, _ENV) - -liquid_level_max = 8 ---+ Calculates the corner levels of a flowingliquid node ---> 4 corner levels from -0.5 to 0.5 as list of `modlib.vector` -function get_liquid_corner_levels(pos) - local node = minetest.get_node(pos) - local def = minetest.registered_nodes[node.name] - local source, flowing = def.liquid_alternative_source, node.name - local range = def.liquid_range or liquid_level_max - local neighbors = {} - for x = -1, 1 do - neighbors[x] = {} - for z = -1, 1 do - local neighbor_pos = {x = pos.x + x, y = pos.y, z = pos.z + z} - local neighbor_node = minetest.get_node(neighbor_pos) - local level - if neighbor_node.name == source then - level = 1 - elseif neighbor_node.name == flowing then - local neighbor_level = neighbor_node.param2 % 8 - level = (math.max(0, neighbor_level - liquid_level_max + range) + 0.5) / range - end - neighbor_pos.y = neighbor_pos.y + 1 - local node_above = minetest.get_node(neighbor_pos) - neighbors[x][z] = { - air = neighbor_node.name == "air", - level = level, - above_is_same_liquid = node_above.name == flowing or node_above.name == source - } - end - end - local function get_corner_level(x, z) - local air_neighbor - local levels = 0 - local neighbor_count = 0 - for nx = x - 1, x do - for nz = z - 1, z do - local neighbor = neighbors[nx][nz] - if neighbor.above_is_same_liquid then - return 1 - end - local level = neighbor.level - if level then - if level == 1 then - return 1 - end - levels = levels + level - neighbor_count = neighbor_count + 1 - elseif neighbor.air then - if air_neighbor then - return 0.02 - end - air_neighbor = true - end - end - end - if neighbor_count == 0 then - return 0 - end - return levels / neighbor_count - end - local corner_levels = { - {0, nil, 0}, - {1, nil, 0}, - {1, nil, 1}, - {0, nil, 1} - } - for index, corner_level in pairs(corner_levels) do - corner_level[2] = get_corner_level(corner_level[1], corner_level[3]) - corner_levels[index] = modlib.vector.subtract_scalar(modlib.vector.new(corner_level), 0.5) - end - return corner_levels -end - -flowing_downwards = modlib.vector.new{0, -1, 0} ---+ Calculates the flow direction of a flowingliquid node ---> `modlib.minetest.flowing_downwards = modlib.vector.new{0, -1, 0}` if only flowing downwards ---> surface direction as `modlib.vector` else -function get_liquid_flow_direction(pos) - local corner_levels = get_liquid_corner_levels(pos) - local max_level = corner_levels[1][2] - for index = 2, 4 do - local level = corner_levels[index][2] - if level > max_level then - max_level = level - end - end - local dir = modlib.vector.new{0, 0, 0} - local count = 0 - for max_level_index, corner_level in pairs(corner_levels) do - if corner_level[2] == max_level then - for offset = 1, 3 do - local index = (max_level_index + offset - 1) % 4 + 1 - local diff = corner_level - corner_levels[index] - if diff[2] ~= 0 then - diff[1] = diff[1] * diff[2] - diff[3] = diff[3] * diff[2] - if offset == 3 then - diff = modlib.vector.divide_scalar(diff, math.sqrt(2)) - end - dir = dir + diff - count = count + 1 - end - end - end - end - if count ~= 0 then - dir = modlib.vector.divide_scalar(dir, count) - end - if dir == modlib.vector.new{0, 0, 0} then - if minetest.get_node(pos).param2 % 32 > 7 then - return flowing_downwards - end - end - return dir -end \ No newline at end of file diff --git a/mods/modlib/minetest/luon.lua b/mods/modlib/minetest/luon.lua deleted file mode 100644 index c44889f4..00000000 --- a/mods/modlib/minetest/luon.lua +++ /dev/null @@ -1,29 +0,0 @@ --- Localize globals -local getmetatable, AreaStore, ItemStack - = getmetatable, AreaStore, ItemStack - --- Metatable lookup for classes specified in lua_api.txt, section "Class reference" -local AreaStoreMT = getmetatable(AreaStore()) -local ItemStackMT = getmetatable(ItemStack"") -local metatables = { - [AreaStoreMT] = {name = "AreaStore", method = AreaStoreMT.to_string}, - [ItemStackMT] = {name = "ItemStack", method = ItemStackMT.to_table}, - -- TODO expand -} - -return modlib.luon.new{ - aux_write = function(_, value) - local type = metatables[getmetatable(value)] - if type then - return type.name, type.method(value) - end - end, - aux_read = { - AreaStore = function(...) - local store = AreaStore() - store:from_string(...) - return store - end, - ItemStack = ItemStack - } -} \ No newline at end of file diff --git a/mods/modlib/minetest/media.lua b/mods/modlib/minetest/media.lua deleted file mode 100644 index bdca19ab..00000000 --- a/mods/modlib/minetest/media.lua +++ /dev/null @@ -1,61 +0,0 @@ -local minetest, modlib, pairs, ipairs - = minetest, modlib, pairs, ipairs - --- TODO support for server texture packs (and possibly client TPs in singleplayer?) -local media_foldernames = {"textures", "sounds", "media", "models", "locale"} -local media_extensions = modlib.table.set{ - -- Textures - "png", "jpg", "bmp", "tga", "pcx", "ppm", "psd", "wal", "rgb"; - -- Sounds - "ogg"; - -- Models - "x", "b3d", "md2", "obj"; - -- Translations - "tr"; -} - -local function collect_media(modname) - local media = {} - local function traverse(folderpath) - -- Traverse files (collect media) - local filenames = minetest.get_dir_list(folderpath, false) - for _, filename in pairs(filenames) do - local _, ext = modlib.file.get_extension(filename) - if media_extensions[ext] then - media[filename] = modlib.file.concat_path{folderpath, filename} - end - end - -- Traverse subfolders - local foldernames = minetest.get_dir_list(folderpath, true) - for _, foldername in pairs(foldernames) do - if not foldername:match"^[_%.]" then -- ignore hidden subfolders / subfolders starting with `_` - traverse(modlib.file.concat_path{folderpath, foldername}) - end - end - end - for _, foldername in ipairs(media_foldernames) do -- order matters! - traverse(modlib.mod.get_resource(modname, foldername)) - end - return media -end - --- TODO clean this up eventually -local paths = {} -local mods = {} -local overridden_paths = {} -local overridden_mods = {} -for _, mod in ipairs(modlib.minetest.get_mod_load_order()) do - local mod_media = collect_media(mod.name) - for medianame, path in pairs(mod_media) do - if paths[medianame] then - overridden_paths[medianame] = overridden_paths[medianame] or {} - table.insert(overridden_paths[medianame], paths[medianame]) - overridden_mods[medianame] = overridden_mods[medianame] or {} - table.insert(overridden_mods[medianame], mods[medianame]) - end - paths[medianame] = path - mods[medianame] = mod.name - end -end - -return {paths = paths, mods = mods, overridden_paths = overridden_paths, overridden_mods = overridden_mods} diff --git a/mods/modlib/minetest/misc.lua b/mods/modlib/minetest/misc.lua deleted file mode 100644 index 3cd4b579..00000000 --- a/mods/modlib/minetest/misc.lua +++ /dev/null @@ -1,328 +0,0 @@ --- Localize globals -local Settings, assert, minetest, modlib, next, pairs, ipairs, string, setmetatable, select, table, type, unpack - = Settings, assert, minetest, modlib, next, pairs, ipairs, string, setmetatable, select, table, type, unpack - --- Set environment -local _ENV = ... -setfenv(1, _ENV) - -max_wear = 2 ^ 16 - 1 - -function override(function_name, function_builder) - local func = minetest[function_name] - minetest["original_" .. function_name] = func - minetest[function_name] = function_builder(func) -end - -local jobs = modlib.heap.new(function(a, b) - return a.time < b.time -end) -local job_metatable = { - __index = { - -- TODO (...) proper (instant rather than deferred) cancellation: - -- Keep index [job] = index, swap with last element and heapify - cancel = function(self) - self.cancelled = true - end - } -} -local time = 0 -function after(seconds, func, ...) - local job = setmetatable({ - time = time + seconds, - func = func, - ["#"] = select("#", ...), - ... - }, job_metatable) - jobs:push(job) - return job -end -minetest.register_globalstep(function(dtime) - time = time + dtime - local job = jobs[1] - while job and job.time <= time do - if not job.cancelled then - job.func(unpack(job, 1, job["#"])) - end - jobs:pop() - job = jobs[1] - end -end) - -function register_globalstep(interval, callback) - if type(callback) ~= "function" then - return - end - local time = 0 - minetest.register_globalstep(function(dtime) - time = time + dtime - if time >= interval then - callback(time) - -- TODO ensure this breaks nothing - time = time % interval - end - end) -end - -form_listeners = {} - -function register_form_listener(formname, func) - local current_listeners = form_listeners[formname] or {} - table.insert(current_listeners, func) - form_listeners[formname] = current_listeners -end - -local icall = modlib.table.icall -minetest.register_on_player_receive_fields(function(player, formname, fields) - icall(form_listeners[formname] or {}, player, fields) -end) - -function texture_modifier_inventorycube(face_1, face_2, face_3) - return "[inventorycube{" .. string.gsub(face_1, "%^", "&") - .. "{" .. string.gsub(face_2, "%^", "&") - .. "{" .. string.gsub(face_3, "%^", "&") -end -function get_node_inventory_image(nodename) - local n = minetest.registered_nodes[nodename] - if not n then - return - end - local tiles = {} - for l, tile in pairs(n.tiles or {}) do - tiles[l] = (type(tile) == "string" and tile) or tile.name - end - local chosen_tiles = { tiles[1], tiles[3], tiles[5] } - if #chosen_tiles == 0 then - return false - end - if not chosen_tiles[2] then - chosen_tiles[2] = chosen_tiles[1] - end - if not chosen_tiles[3] then - chosen_tiles[3] = chosen_tiles[2] - end - local img = minetest.registered_items[nodename].inventory_image - if string.len(img) == 0 then - img = nil - end - return img or texture_modifier_inventorycube(chosen_tiles[1], chosen_tiles[2], chosen_tiles[3]) -end -function check_player_privs(playername, privtable) - local privs=minetest.get_player_privs(playername) - local missing_privs={} - local to_lose_privs={} - for priv, expected_value in pairs(privtable) do - local actual_value=privs[priv] - if expected_value then - if not actual_value then - table.insert(missing_privs, priv) - end - else - if actual_value then - table.insert(to_lose_privs, priv) - end - end - end - return missing_privs, to_lose_privs -end - ---+ Improved base64 decode removing valid padding -function decode_base64(base64) - local len = base64:len() - local padding_char = base64:sub(len, len) == "=" - if padding_char then - if len % 4 ~= 0 then - return - end - if base64:sub(len-1, len-1) == "=" then - base64 = base64:sub(1, len-2) - else - base64 = base64:sub(1, len-1) - end - end - return minetest.decode_base64(base64) -end - -local object_refs = minetest.object_refs ---+ Objects inside radius iterator. Uses a linear search. -function objects_inside_radius(pos, radius) - radius = radius^2 - local id, object, object_pos - return function() - repeat - id, object = next(object_refs, id) - object_pos = object:get_pos() - until (not object) or ((pos.x-object_pos.x)^2 + (pos.y-object_pos.y)^2 + (pos.z-object_pos.z)^2) <= radius - return object - end -end - ---+ Objects inside area iterator. Uses a linear search. -function objects_inside_area(min, max) - local id, object, object_pos - return function() - repeat - id, object = next(object_refs, id) - object_pos = object:get_pos() - until (not object) or ( - (min.x <= object_pos.x and min.y <= object_pos.y and min.z <= object_pos.z) - and - (max.y >= object_pos.x and max.y >= object_pos.y and max.z >= object_pos.z) - ) - return object - end -end - ---: node_or_groupname "modname:nodename", "group:groupname[,groupname]" ---> function(nodename) -> whether node matches -function nodename_matcher(node_or_groupname) - if modlib.text.starts_with(node_or_groupname, "group:") then - local groups = modlib.text.split(node_or_groupname:sub(("group:"):len() + 1), ",") - return function(nodename) - for _, groupname in pairs(groups) do - if minetest.get_item_group(nodename, groupname) == 0 then - return false - end - end - return true - end - else - return function(nodename) - return nodename == node_or_groupname - end - end -end - -do - local default_create, default_free = function() return {} end, modlib.func.no_op - local metatable = {__index = function(self, player) - if type(player) == "userdata" then - return self[player:get_player_name()] - end - end} - function playerdata(create, free) - create = create or default_create - free = free or default_free - local data = {} - minetest.register_on_joinplayer(function(player) - data[player:get_player_name()] = create(player) - end) - minetest.register_on_leaveplayer(function(player) - data[player:get_player_name()] = free(player) - end) - setmetatable(data, metatable) - return data - end -end - -function connected_players() - -- TODO cache connected players - local connected_players = minetest.get_connected_players() - local index = 0 - return function() - index = index + 1 - return connected_players[index] - end -end - -function set_privs(name, priv_updates) - local privs = minetest.get_player_privs(name) - for priv, grant in pairs(priv_updates) do - if grant then - privs[priv] = true - else - -- May not be set to false; Minetest treats false as truthy in this instance - privs[priv] = nil - end - end - return minetest.set_player_privs(name, privs) -end - -function register_on_leaveplayer(func) - return minetest["register_on_" .. (minetest.is_singleplayer() and "shutdown" or "leaveplayer")](func) -end - -do local mod_info -function get_mod_info() - if mod_info then return mod_info end - mod_info = {} - -- TODO validate modnames - local modnames = minetest.get_modnames() - for _, mod in pairs(modnames) do - local info - local function read_file(filename) - return modlib.file.read(modlib.mod.get_resource(mod, filename)) - end - local mod_conf = Settings(modlib.mod.get_resource(mod, "mod.conf")) - if mod_conf then - info = {} - mod_conf = mod_conf:to_table() - local function read_depends(field) - local depends = {} - for depend in (mod_conf[field] or ""):gmatch"[^,]+" do - depends[modlib.text.trim_spacing(depend)] = true - end - info[field] = depends - end - read_depends"depends" - read_depends"optional_depends" - else - info = { - description = read_file"description.txt", - depends = {}, - optional_depends = {} - } - local depends_txt = read_file"depends.txt" - if depends_txt then - for _, dependency in ipairs(modlib.table.map(modlib.text.split(depends_txt or "", "\n"), modlib.text.trim_spacing)) do - local modname, is_optional = dependency:match"(.+)(%??)" - table.insert(is_optional == "" and info.depends or info.optional_depends, modname) - end - end - end - if info.name == nil then - info.name = mod - end - mod_info[mod] = info - end - return mod_info -end end - -do local mod_load_order -function get_mod_load_order() - if mod_load_order then return mod_load_order end - mod_load_order = {} - local mod_info = get_mod_info() - -- If there are circular soft dependencies, it is possible that a mod is loaded, but not in the right order - -- TODO somehow maximize the number of soft dependencies fulfilled in case of circular soft dependencies - local function load(mod) - if mod.status == "loaded" then - return true - end - if mod.status == "loading" then - return false - end - -- TODO soft/vs hard loading status, reset? - mod.status = "loading" - -- Try hard dependencies first. These must be fulfilled. - for depend in pairs(mod.depends) do - if not load(mod_info[depend]) then - return false - end - end - -- Now, try soft dependencies. - for depend in pairs(mod.optional_depends) do - -- Mod may not exist - if mod_info[depend] then - load(mod_info[depend]) - end - end - mod.status = "loaded" - table.insert(mod_load_order, mod) - return true - end - for _, mod in pairs(mod_info) do - assert(load(mod)) - end - return mod_load_order -end end diff --git a/mods/modlib/minetest/mod.lua b/mods/modlib/minetest/mod.lua deleted file mode 100644 index a321e530..00000000 --- a/mods/modlib/minetest/mod.lua +++ /dev/null @@ -1,184 +0,0 @@ --- Localize globals -local Settings, _G, assert, dofile, error, getmetatable, ipairs, loadfile, loadstring, minetest, modlib, pairs, rawget, rawset, setfenv, setmetatable, tonumber, type, table_concat, unpack - = Settings, _G, assert, dofile, error, getmetatable, ipairs, loadfile, loadstring, minetest, modlib, pairs, rawget, rawset, setfenv, setmetatable, tonumber, type, table.concat, unpack - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - -local loaded = {} -function require(filename) - local modname = minetest.get_current_modname() - loaded[modname] = loaded[modname] or {} - -- Minetest ensures that `/` works even on Windows (path normalization) - loaded[modname][filename] = loaded[modname][filename] -- already loaded? - or dofile(minetest.get_modpath(modname) .. "/" .. filename:gsub("%.", "/") .. ".lua") - return loaded[modname][filename] -end - -function loadfile_exports(filename) - local env = setmetatable({}, {__index = _G}) - local file = assert(loadfile(filename)) - setfenv(file, env) - file() - return env -end - --- get resource + dofile -function include(modname, file) - if not file then - file = modname - modname = minetest.get_current_modname() - end - return dofile(get_resource(modname, file)) -end - -function include_env(file_or_string, env, is_string) - setfenv(assert((is_string and loadstring or loadfile)(file_or_string)), env)() -end - -function create_namespace(namespace_name, parent_namespace) - namespace_name = namespace_name or minetest.get_current_modname() - parent_namespace = parent_namespace or _G - local metatable = {__index = parent_namespace == _G and function(_, key) return rawget(_G, key) end or parent_namespace} - local namespace = {} - namespace = setmetatable(namespace, metatable) - if parent_namespace == _G then - rawset(parent_namespace, namespace_name, namespace) - else - parent_namespace[namespace_name] = namespace - end - return namespace -end - --- formerly extend_mod -function extend(modname, file) - if not file then - file = modname - modname = minetest.get_current_modname() - end - include_env(get_resource(modname, file .. ".lua"), rawget(_G, modname)) -end - --- runs main.lua in table env --- formerly include_mod -function init(modname) - modname = modname or minetest.get_current_modname() - create_namespace(modname) - extend(modname, "main") -end - --- TODO `require` relative to current mod - -local warn_parent_leaf = "modlib: setting %s used both as parent setting and as leaf, ignoring children" -local function build_tree(dict) - local tree = {} - for key, value in pairs(dict) do - local path = modlib.text.split_unlimited(key, ".", true) - local subtree = tree - for i = 1, #path - 1 do - local index = tonumber(path[i]) or path[i] - subtree[index] = subtree[index] or {} - subtree = subtree[index] - if type(subtree) ~= "table" then - minetest.log("warning", warn_parent_leaf:format(table_concat({unpack(path, 1, i)}, "."))) - break - end - end - if type(subtree) == "table" then - if type(subtree[path[#path]]) == "table" then - minetest.log("warning", warn_parent_leaf:format(key)) - end - subtree[path[#path]] = value - end - end - return tree -end - -settings = build_tree(minetest.settings:to_table()) - ---> conf, schema -function configuration(modname) - modname = modname or minetest.get_current_modname() - local schema = modlib.schema.new(assert(include(modname, "schema.lua"))) - schema.name = schema.name or modname - local settingtypes = schema:generate_settingtypes() - assert(schema.type == "table") - local overrides = {} - local conf - local function add(path) - for _, format in ipairs{ - {extension = "lua", read = function(text) - assert(overrides._C == nil) - local additions = setfenv(assert(loadstring(text)), setmetatable(overrides, {__index = {_C = overrides}}))() - setmetatable(overrides, nil) - if additions == nil then - return overrides - end - return additions - end}, - {extension = "luon", read = function(text) - local value = {setfenv(assert(loadstring("return " .. text)), setmetatable(overrides, {}))()} - assert(#value == 1) - value = value[1] - local function check_type(value) - local type = type(value) - if type == "table" then - assert(getmetatable(value) == nil) - for key, value in pairs(value) do - check_type(key) - check_type(value) - end - elseif not (type == "boolean" or type == "number" or type == "string") then - error("disallowed type " .. type) - end - end - check_type(value) - return value - end}, - {extension = "conf", read = function(text) - return build_tree(Settings(text):to_table()) - end, convert_strings = true}, - {extension = "json", read = minetest.parse_json} - } do - local content = modlib.file.read(path .. "." .. format.extension) - if content then - overrides = modlib.table.deep_add_all(overrides, format.read(content)) - conf = schema:load(overrides, {convert_strings = format.convert_strings, error_message = true}) - end - end - end - add(minetest.get_worldpath() .. "/conf/" .. modname) - add(get_resource(modname, "conf")) - local minetest_conf = settings[schema.name] - if minetest_conf then - overrides = modlib.table.deep_add_all(overrides, minetest_conf) - conf = schema:load(overrides, {convert_strings = true, error_message = true}) - end - modlib.file.ensure_content(get_resource(modname, "settingtypes.txt"), settingtypes) - local readme_path = get_resource(modname, "Readme.md") - local readme = modlib.file.read(readme_path) - if readme then - local modified = false - readme = readme:gsub("" .. "(.-)" .. "", function(level, content) - schema._md_level = assert(tonumber(level)) + 1 - -- HACK: Newline between comment and heading (MD implementations don't handle comments properly) - local markdown = "\n" .. schema:generate_markdown() - if content ~= markdown then - modified = true - return "" .. markdown .. "" - end - end, 1) - if modified then - -- FIXME mod security messes with this (disallows it if enabled) - assert(modlib.file.write(readme_path, readme)) - end - end - if conf == nil then - return schema:load({}, {error_message = true}), schema - end - return conf, schema -end - --- Export environment -return _ENV \ No newline at end of file diff --git a/mods/modlib/minetest/obj.lua b/mods/modlib/minetest/obj.lua deleted file mode 100644 index 82665101..00000000 --- a/mods/modlib/minetest/obj.lua +++ /dev/null @@ -1,190 +0,0 @@ -local assert, tonumber, type, setmetatable, ipairs, unpack - = assert, tonumber, type, setmetatable, ipairs, unpack - -local math_floor, table_insert, table_concat - = math.floor, table.insert, table.concat - -local obj = {} - -local metatable = {__index = obj} - -local function read_floats(next_word, n) - if n == 0 then return end - local num = next_word() - assert(num:find"^%-?%d+$" or num:find"^%-?%d+%.%d+$") - return tonumber(num), read_floats(next_word, n - 1) -end - -local function read_index(list, index) - if not index then return end - index = tonumber(index) - if index < 0 then - index = index + #list + 1 - end - assert(list[index]) - return index -end - -local function read_indices(self, next_word) - local word = next_word() - if not word then return end - -- TODO optimize this (ideally using a vararg-ish split by `/`) - local vertex, texcoord, normal - vertex = word:match"^%-?%d+$" - if not vertex then - vertex, texcoord = word:match"^(%-?%d+)/(%-?%d+)$" - if not vertex then - vertex, normal = word:match"^(%-?%d+)//(%-?%d+)$" - if not vertex then - vertex, texcoord, normal = word:match"^(%-?%d+)/(%-?%d+)/(%-?%d+)$" - end - end - end - return { - vertex = read_index(self.vertices, vertex), - texcoord = read_index(self.texcoords, texcoord), - normal = read_index(self.normals, normal) - }, read_indices(self, next_word) -end - -function obj.read_lines( - ... -- line iterator such as `modlib.text.lines"str"` or `io.lines"filename"` -) - local self = { - vertices = {}, - texcoords = {}, - normals = {}, - groups = {} - } - local groups = {} - local active_group = {name = "default"} - groups[1] = active_group - groups.default = active_group - for line in ... do - if line:byte() ~= ("#"):byte() then - local next_word = line:gmatch"%S+" - local command = next_word() - if command == "v" or command == "vn" then - local x, y, z = read_floats(next_word, 3) - x = -x - table_insert(self[command == "v" and "vertices" or "normals"], {x, y, z}) - elseif command == "vt" then - local x, y = read_floats(next_word, 2) - y = 1 - y - table_insert(self.texcoords, {x, y}) - elseif command == "f" then - table_insert(active_group, {read_indices(self, next_word)}) - elseif command == "g" or command == "usemtl" then - -- TODO consider distinguishing between materials & groups - local name = next_word() or "default" - if groups[name] then - active_group = groups[name] - else - active_group = {name = name} - table_insert(groups, active_group) - groups[name] = active_group - end - assert(not next_word(), "only a single group/material name is supported") - end - end - end - -- Keep only nonempty groups - for _, group in ipairs(groups) do - if group[1] ~= nil then - table_insert(self.groups, group) - end - end - return setmetatable(self, metatable) -- obj object -end - --- Does not close a file handle if passed ---> obj object -function obj.read_file(file_or_name) - if type(file_or_name) == "string" then - return obj.read_lines(io.lines(file_or_name)) - end - local handle = file_or_name - -- `handle.read, handle` can be used as a line iterator - return obj.read_lines(assert(handle.read), handle) -end - ---> obj object -function obj.read_string(str) - -- Empty lines can be ignored - return obj.read_lines(str:gmatch"[^\r\n]+") -end - -local function write_float(float) - if math_floor(float) == float then - return ("%d"):format(float) - end - return ("%f"):format(float):match"^(.-)0*$" -- strip trailing zeros -end - -local function write_index(index) - if index.texcoord then - if index.normal then - return("%d/%d/%d"):format(index.vertex, index.texcoord, index.normal) - end - return ("%d/%d"):format(index.vertex, index.texcoord) - end if index.normal then - return ("%d//%d"):format(index.vertex, index.normal) - end - return ("%d"):format(index.vertex) -end - --- Callback/"caller"-style iterator; use `iterator.for_generator` to turn this into a callee-style iterator -function obj:write_lines( - write_line -- function(line: string) to write a line -) - local function write_v3f(type, v3f) - local x, y, z = unpack(v3f) - x = -x - write_line(("%s %s %s %s"):format(type, write_float(x), write_float(y), write_float(z))) - end - for _, vertex in ipairs(self.vertices) do - write_v3f("v", vertex) - end - for _, normal in ipairs(self.normals) do - write_v3f("vn", normal) - end - for _, texcoord in ipairs(self.texcoords) do - local x, y = texcoord[1], texcoord[2] - y = 1 - y - write_line(("vt %s %s"):format(write_float(x), write_float(y))) - end - for _, group in ipairs(self.groups) do - write_line("g " .. group.name) -- this will convert `usemtl` into `g` but that shouldn't matter - for _, face in ipairs(group) do - local command = {"f"} - for i, index in ipairs(face) do - command[i + 1] = write_index(index) - end - write_line(table_concat(command, " ")) - end - end -end - --- Write `self` to a file --- Does not close or flush a file handle if passed -function obj:write_file(file_or_name) - if type(file_or_name) == "string" then - file_or_name = io.open(file_or_name) - end - self:write_lines(function(line) - file_or_name:write(line) - file_or_name:write"\n" - end) -end - --- Write `self` to a string -function obj:write_string() - local rope = {} - self:write_lines(function(line) - table_insert(rope, line) - end) - table_insert(rope, "") -- trailing newline for good measure - return table_concat(rope, "\n") -- string representation of `self` -end - -return obj \ No newline at end of file diff --git a/mods/modlib/minetest/png.lua b/mods/modlib/minetest/png.lua deleted file mode 100644 index bdec662e..00000000 --- a/mods/modlib/minetest/png.lua +++ /dev/null @@ -1,483 +0,0 @@ -local signature = "\137\80\78\71\13\10\26\10" - -local assert, char, ipairs, insert, concat, abs, floor = assert, string.char, ipairs, table.insert, table.concat, math.abs, math.floor - --- TODO move to modlib.bit eventually -local function bit_xor(a, b) - local res = 0 - local bit = 1 - for _ = 1, 32 do - if a % 2 ~= b % 2 then - res = res + bit - end - a = floor(a / 2) - b = floor(b / 2) - bit = bit * 2 - end - return res -end - --- Try to use `bit` library (if available) for a massive speed boost -local bit = rawget(_G, "bit") -if bit then - local bxor = bit.bxor - function bit_xor(a, b) - local res = bxor(a, b) - if res < 0 then -- convert signed to unsigned - return res + 2^32 - end - return res - end -end - -local crc_table = {} -for i = 0, 255 do - local c = i - for _ = 0, 7 do - if c % 2 > 0 then - c = bit_xor(0xEDB88320, floor(c / 2)) - else - c = floor(c / 2) - end - end - crc_table[i] = c -end - -local function update_crc(crc, text) - for i = 1, #text do - crc = bit_xor(crc_table[bit_xor(crc % 0x100, text:byte(i))], floor(crc / 0x100)) - end - return crc -end - - -local color_types = { - [0] = { - color = "grayscale" - }, - [2] = { - color = "truecolor" - }, - [3] = { - color = "palette", - depth = 8 - }, - [4] = { - color = "grayscale", - alpha = true - }, - [6] = { - color = "truecolor", - alpha = true - } -} -local set = modlib.table.set -local allowed_bit_depths = { - [0] = set{1, 2, 4, 8, 16}, - [2] = set{8, 16}, - [3] = set{1, 2, 4, 8}, - [4] = set{8, 16}, - [6] = set{8, 16} -} -local samples = { - grayscale = 1, - palette = 1, - truecolor = 3 -} - -local adam7_passes = { - x_min = { 0, 4, 0, 2, 0, 1, 0 }, - y_min = { 0, 0, 4, 0, 2, 0, 1 }, - x_step = { 8, 8, 4, 4, 2, 2, 1 }, - y_step = { 8, 8, 8, 4, 4, 2, 2 }, -}; - -(...).decode_png = function(stream) - local chunk_crc - local function read(n) - local text = stream:read(n) - assert(#text == n) - if chunk_crc then - chunk_crc = update_crc(chunk_crc, text) - end - return text - end - local function byte() - return read(1):byte() - end - local function _uint() - return 0x1000000 * byte() + 0x10000 * byte() + 0x100 * byte() + byte() - end - local function uint() - local val = _uint() - assert(val < 2^31, "uint out of range") - return val - end - local function check_crc() - local crc = chunk_crc - chunk_crc = nil - if _uint() ~= bit_xor(crc, 0xFFFFFFFF) then - error("CRC mismatch", 2) - end - end - - assert(read(8) == signature, "PNG signature expected") - - local IHDR_len = uint() - assert(IHDR_len == 13, "invalid IHDR length") - chunk_crc = 0xFFFFFFFF - assert(read(4) == "IHDR", "IHDR chunk expected") - local width = uint() - assert(width > 0) - local height = uint() - assert(height > 0) - local bit_depth = byte() - local color_type_number = byte() - local color_type = assert(color_types[color_type_number], "invalid color type") - if color_type.color ~= "palette" then - color_type.depth = bit_depth - end - assert(allowed_bit_depths[color_type_number][bit_depth], "disallowed bit depth for color type") - local compression_method = byte() - assert(compression_method == 0, "unsupported compression method") - local filter_method = byte() - assert(filter_method == 0, "unsupported filter method") - local interlace_method = byte() - assert(interlace_method <= 1, "unsupported interlace method") - local adam7 = interlace_method == 1 - check_crc() -- IHDR CRC - - local palette - local alpha - local source_gamma - local idat_content = {} - local idat_allowed = true - local iend - repeat - local chunk_length = uint() - chunk_crc = 0xFFFFFFFF - local chunk_type = read(4) - if chunk_type == "IDAT" then - assert(idat_allowed, "no chunks inbetween IDAT chunks allowed") - if color_type.color == "palette" then - assert(palette, "PLTE chunk expected") - end - insert(idat_content, read(chunk_length)) - else - if next(idat_content) then - -- Non-IDAT chunk, no IDAT chunks allowed anymore - idat_allowed = false - end - if chunk_type == "PLTE" then - assert(color_type.color ~= "grayscale") - assert(not palette, "double PLTE chunk") - assert(idat_allowed, "PLTE after IDAT chunks") - palette = {} - local entries = chunk_length / 3 - assert(entries % 1 == 0 and entries >= 1 and entries <= 2^bit_depth, "invalid PLTE chunk length") - for i = 1, entries do - palette[i] = 0x10000 * byte() + 0x100 * byte() + byte() -- RGB - end - elseif chunk_type == "tRNS" then - assert(not color_type.alpha, "unexpected tRNS chunk") - color_type.transparency = true - assert(idat_allowed, "tRNS after IDAT chunks") - if color_type.color == "palette" then - assert(palette, "PLTE chunk expected") - alpha = {} - for i = 1, chunk_length do - alpha[i] = byte() - end - elseif color_type.color == "grayscale" then - assert(chunk_length == 2) - alpha = 0x100 * byte() + byte() - else - assert(color_type.color == "truecolor") - assert(chunk_length == 6) - alpha = 0 - -- Read 16-bit RGB (6 bytes) - for _ = 1, 6 do - alpha = alpha * 0x100 + byte() - end - end - elseif chunk_type == "gAMA" then - assert(not palette, "gAMA after PLTE chunk") - assert(idat_allowed, "gAMA after IDAT chunks") - assert(chunk_length == 4) - source_gamma = uint() / 1e5 - elseif chunk_type == "IEND" then - iend = true - else - -- Check whether the fifth bit of the first byte is set (upper vs. lowercase ASCII) - local ancillary = floor(chunk_type:byte(1) % (2^6)) >= 2^5 - if not ancillary then - error(("unsupported critical chunk: %q"):format(chunk_type)) - end - read(chunk_length) - end - end - check_crc() - until iend - assert(next(idat_content), "no IDAT chunk") - idat_content = minetest.decompress(concat(idat_content), "deflate") - --[[ - For memory efficiency, we try to pack everything in a single number: - Grayscale/lightness: AY - Palette: ARGB - Truecolor (8-bit): ARGB - Truecolor (16-bit): RGB + A - (64 bits required, packing non-mantissa bits isn't practical) => separate table with alpha values - ]] - local data = {} - local alpha_data - if color_type.color == "truecolor" and bit_depth == 16 and (color_type.alpha or color_type.transparency) then - alpha_data = {} - end - if adam7 then - -- Allocate space in list part in order to not fill the hash part later - for i = 1, width * height do - data[i] = false - if alpha_data then - alpha_data[i] = false - end - end - end - local bits_per_pixel = (samples[color_type.color] + (color_type.alpha and 1 or 0)) * bit_depth - local bytes_per_pixel = math.ceil(bits_per_pixel / 8) - local previous_scanline - local idat_base_index = 1 - local function read_scanline(x_min, x_step, y) - local scanline_width = math.ceil((width - x_min) / x_step) - local scanline_bytecount = math.ceil(scanline_width * bits_per_pixel / 8) - local filtering = idat_content:byte(idat_base_index) - local scanline = {} - for i = 1, scanline_bytecount do - local val = idat_content:byte(idat_base_index + i) - local left = scanline[i - bytes_per_pixel] or 0 - local up = previous_scanline and previous_scanline[i] or 0 - local left_up = previous_scanline and previous_scanline[i - bytes_per_pixel] or 0 - -- Undo lossless filter - if filtering == 0 then -- None - scanline[i] = val - elseif filtering == 1 then -- Sub - scanline[i] = (left + val) % 0x100 - elseif filtering == 2 then -- Up - scanline[i] = (up + val) % 0x100 - elseif filtering == 3 then -- Average - scanline[i] = (floor((left + up) / 2) + val) % 0x100 - elseif filtering == 4 then -- Paeth - local p = left + up - left_up - local p_left = abs(p - left) - local p_up = abs(p - up) - local p_left_up = abs(p - left_up) - local p_res - if p_left <= p_up and p_left <= p_left_up then - p_res = left - elseif p_up <= p_left_up then - p_res = up - else - p_res = left_up - end - scanline[i] = (p_res + val) % 0x100 - else - error("invalid filtering method: " .. filtering) - end - assert(scanline[i] >= 0 and scanline[i] <= 255 and scanline[i] % 1 == 0) - end - local bit = 0 - local function sample() - local byte_idx = 1 + floor(bit / 8) - bit = bit + bit_depth - local byte = scanline[byte_idx] - if bit_depth == 16 then - return byte * 0x100 + scanline[byte_idx + 1] - end - if bit_depth == 8 then - return byte - end - assert(bit_depth == 1 or bit_depth == 2 or bit_depth == 4) - local low = 2^(-bit % 8) - return floor(byte / low) % (2^bit_depth) - end - for x = x_min, width - 1, x_step do - local data_index = y * width + x + 1 - if color_type.color == "palette" then - local palette_index = sample() - local rgb = assert(palette[palette_index + 1], "palette index out of range") - -- Index alpha table if available - local a = alpha and alpha[palette_index + 1] or 255 - data[data_index] = a * 0x1000000 + rgb - elseif color_type.color == "grayscale" then - local Y = sample() - local a = 2^bit_depth - 1 - if color_type.alpha then - a = sample() - elseif alpha == Y then - a = 0 -- Convert grayscale to transparency - end - data[data_index] = a * (2^bit_depth) + Y - else - assert(color_type.color == "truecolor") - local r, g, b = sample(), sample(), sample() - local rgb16 = r * 0x100000000 + g * 0x10000 + b - local a = 2^bit_depth - 1 - if color_type.alpha then - a = sample() - elseif alpha == rgb16 then - a = 0 -- Convert color to transparency - end - if bit_depth == 8 then - data[data_index] = a * 0x1000000 + r * 0x10000 + g * 0x100 + b - else - assert(bit_depth == 16) - -- Pack only RGB in data, alpha goes in a different table - -- 3 * 16 = 48 bytes can still be held accurately by the double mantissa - data[data_index] = rgb16 - if alpha_data then - alpha_data[data_index] = a - end - end - end - end - -- Each byte of the scanline must have been read from - assert(bit >= #scanline * 8 - 7) - previous_scanline = scanline - idat_base_index = idat_base_index + scanline_bytecount + 1 - end - if adam7 then - for pass = 1, 7 do - local x_min, y_min = adam7_passes.x_min[pass], adam7_passes.y_min[pass] - if x_min < width and y_min < height then -- Non-empty pass - local x_step, y_step = adam7_passes.x_step[pass], adam7_passes.y_step[pass] - previous_scanline = nil -- Filtering doesn't use scanlines of previous passes - for y = y_min, height - 1, y_step do - read_scanline(x_min, x_step, y) - end - end - end - else - for y = 0, height - 1 do - read_scanline(0, 1, y) - end - end - return { - width = width, - height = height, - color_type = color_type, - source_gamma = source_gamma, - data = data, - alpha_data = alpha_data - } -end - -local function rescale_depth(sample, source_depth, target_depth) - if source_depth == target_depth then - return sample - end - return floor((sample * (2^target_depth - 1) / (2^source_depth - 1)) + 0.5) -end --- In-place lossy (if bit depth = 16) conversion to ARGB8 -(...).convert_png_to_argb8 = function(png) - local color, transparency, depth = png.color_type.color, png.color_type.alpha or png.color_type.transparency, png.color_type.depth - if color == "palette" or (color == "truecolor" and depth == 8) then - return - end - for index, value in pairs(png.data) do - if color == "grayscale" then - local a, Y = rescale_depth(floor(value / (2^depth)), depth, 8), rescale_depth(value % (2^depth), depth, 8) - png.data[index] = a * 0x1000000 + Y * 0x10000 + Y * 0x100 + Y -- R = G = B = Y - else - assert(color == "truecolor" and depth == 16) - local r = rescale_depth(floor(value / 0x100000000), depth, 8) - local g = rescale_depth(floor(value / 0x10000) % 0x10000, depth, 8) - local b = rescale_depth(value % 0x10000, depth, 8) - local a = 0xFF - if transparency then - a = rescale_depth(png.alpha_data[index], depth, 8) - end - png.data[index] = a * 0x1000000 + r * 0x10000 + g * 0x100 + b - end - end - png.color_type = color_types[6] - png.bit_depth = 8 - png.alpha_data = nil -end - -local function encode_png(width, height, data, compression, raw_write) - local write = raw_write - local function byte(value) - write(char(value)) - end - local function _uint(value) - local div = 0x1000000 - for _ = 1, 4 do - byte(floor(value / div) % 0x100) - div = div / 0x100 - end - end - local function uint(value) - assert(value < 2^31) - _uint(value) - end - local chunk_content - local function chunk_write(text) - insert(chunk_content, text) - end - local function chunk(type) - chunk_content = {} - write = chunk_write - write(type) - end - local function end_chunk() - write = raw_write - local chunk_len = 0 - for i = 2, #chunk_content do - chunk_len = chunk_len + #chunk_content[i] - end - uint(chunk_len) - write(concat(chunk_content)) - local chunk_crc = 0xFFFFFFFF - for _, text in ipairs(chunk_content) do - chunk_crc = update_crc(chunk_crc, text) - end - _uint(bit_xor(chunk_crc, 0xFFFFFFFF)) - end - -- Signature - write(signature) - chunk"IHDR" - uint(width) - uint(height) - -- Always use bit depth 8 - byte(8) - -- Always use color type "truecolor with alpha" - byte(6) - -- Compression method: deflate - byte(0) - -- Filter method: PNG filters - byte(0) - -- No interlace - byte(0) - end_chunk() - chunk"IDAT" - local data_rope = {} - for y = 0, height - 1 do - local base_index = y * width - insert(data_rope, "\0") - for x = 1, width do - local colorspec = modlib.minetest.colorspec.from_any(data[base_index + x]) - insert(data_rope, char(colorspec.r, colorspec.g, colorspec.b, colorspec.a)) - end - end - write(minetest.compress(type(data) == "string" and data or concat(data_rope), "deflate", compression)) - end_chunk() - chunk"IEND" - end_chunk() -end - -(...).encode_png = minetest.encode_png or function(width, height, data, compression) - local rope = {} - encode_png(width, height, data, compression or 9, function(text) - insert(rope, text) - end) - return concat(rope) -end diff --git a/mods/modlib/minetest/raycast.lua b/mods/modlib/minetest/raycast.lua deleted file mode 100644 index 5c3617c8..00000000 --- a/mods/modlib/minetest/raycast.lua +++ /dev/null @@ -1,137 +0,0 @@ --- Localize globals -local assert, math, minetest, modlib, pairs, setmetatable, vector = assert, math, minetest, modlib, pairs, setmetatable, vector - ---+ Raycast wrapper with proper flowingliquid intersections -return function(_pos1, _pos2, objects, liquids) - local raycast = minetest.raycast(_pos1, _pos2, objects, liquids) - if not liquids then - return raycast - end - local pos1 = modlib.vector.from_minetest(_pos1) - local _direction = vector.direction(_pos1, _pos2) - local direction = modlib.vector.from_minetest(_direction) - local length = vector.distance(_pos1, _pos2) - local function next() - local pointed_thing = raycast:next() - if (not pointed_thing) or pointed_thing.type ~= "node" then - return pointed_thing - end - local _pos = pointed_thing.under - local pos = modlib.vector.from_minetest(_pos) - local node = minetest.get_node(_pos) - local def = minetest.registered_nodes[node.name] - if not (def and def.drawtype == "flowingliquid") then - return pointed_thing - end - local corner_levels = modlib.minetest.get_liquid_corner_levels(_pos) - local full_corner_levels = true - for _, corner_level in pairs(corner_levels) do - if corner_level[2] < 0.5 then - full_corner_levels = false - break - end - end - if full_corner_levels then - return pointed_thing - end - local relative = pos1 - pos - local inside = true - for _, prop in pairs(relative) do - if prop <= -0.5 or prop >= 0.5 then - inside = false - break - end - end - local function level(x, z) - local function distance_squared(corner) - return (x - corner[1]) ^ 2 + (z - corner[3]) ^ 2 - end - local irrelevant_corner, distance = 1, distance_squared(corner_levels[1]) - for index = 2, 4 do - local other_distance = distance_squared(corner_levels[index]) - if other_distance > distance then - irrelevant_corner, distance = index, other_distance - end - end - local function corner(off) - return corner_levels[((irrelevant_corner + off) % 4) + 1] - end - local base = corner(2) - local edge_1, edge_2 = corner(1) - base, corner(3) - base - -- Properly selected edges will have a total length of 2 - assert(math.abs(edge_1[1] + edge_1[3]) + math.abs(edge_2[1] + edge_2[3]) == 2) - if edge_1[1] == 0 then - edge_1, edge_2 = edge_2, edge_1 - end - local level = base[2] + (edge_1[2] * ((x - base[1]) / edge_1[1])) + (edge_2[2] * ((z - base[3]) / edge_2[3])) - assert(level >= -0.5 and level <= 0.5) - return level - end - inside = inside and (relative[2] < level(relative[1], relative[3])) - if inside then - -- pos1 is inside the liquid node - pointed_thing.intersection_point = _pos1 - pointed_thing.intersection_normal = vector.new(0, 0, 0) - return pointed_thing - end - local function intersection_normal(axis, dir) - return {x = 0, y = 0, z = 0, [axis] = dir} - end - local function plane(axis, dir) - local offset = dir * 0.5 - local diff_axis = (relative[axis] - offset) / -direction[axis] - local intersection_point = {} - for plane_axis = 1, 3 do - if plane_axis ~= axis then - local value = direction[plane_axis] * diff_axis + relative[plane_axis] - if value < -0.5 or value > 0.5 then - return - end - intersection_point[plane_axis] = value - end - end - intersection_point[axis] = offset - return intersection_point - end - if direction[2] > 0 then - local intersection_point = plane(2, -1) - if intersection_point then - pointed_thing.intersection_point = (intersection_point + pos):to_minetest() - pointed_thing.intersection_normal = intersection_normal("y", -1) - return pointed_thing - end - end - for coord, other in pairs{[1] = 3, [3] = 1} do - if direction[coord] ~= 0 then - local dir = direction[coord] > 0 and -1 or 1 - local intersection_point = plane(coord, dir) - if intersection_point then - local height = 0 - for _, corner in pairs(corner_levels) do - if corner[coord] == dir * 0.5 then - height = height + (math.abs(intersection_point[other] + corner[other])) * corner[2] - end - end - if intersection_point[2] <= height then - pointed_thing.intersection_point = (intersection_point + pos):to_minetest() - pointed_thing.intersection_normal = intersection_normal(modlib.vector.index_aliases[coord], dir) - return pointed_thing - end - end - end - end - for _, triangle in pairs{ - {corner_levels[3], corner_levels[2], corner_levels[1]}, - {corner_levels[4], corner_levels[3], corner_levels[1]} - } do - local pos_on_ray = modlib.vector.ray_triangle_intersection(relative, direction, triangle) - if pos_on_ray and pos_on_ray <= length then - pointed_thing.intersection_point = (pos1 + modlib.vector.multiply_scalar(direction, pos_on_ray)):to_minetest() - pointed_thing.intersection_normal = modlib.vector.triangle_normal(triangle):to_minetest() - return pointed_thing - end - end - return next() - end - return setmetatable({next = next}, {__call = next}) -end diff --git a/mods/modlib/minetest/schematic.lua b/mods/modlib/minetest/schematic.lua deleted file mode 100644 index eb7815dc..00000000 --- a/mods/modlib/minetest/schematic.lua +++ /dev/null @@ -1,193 +0,0 @@ --- Localize globals -local VoxelArea, ItemStack, assert, error, io, ipairs, math, minetest, modlib, next, pairs, setmetatable, string, table, type, vector - = VoxelArea, ItemStack, assert, error, io, ipairs, math, minetest, modlib, next, pairs, setmetatable, string, table, type, vector - - -local schematic = {} -local metatable = {__index = schematic} - -function schematic.setmetatable(self) - return setmetatable(self, metatable) -end - -function schematic.create(params, pos_min, pos_max) - pos_min, pos_max = vector.sort(pos_min, pos_max) - local size = vector.add(vector.subtract(pos_max, pos_min), 1) - local voxelmanip = minetest.get_voxel_manip(pos_min, pos_max) - local emin, emax = voxelmanip:read_from_map(pos_min, pos_max) - local voxelarea = VoxelArea:new{ MinEdge = emin, MaxEdge = emax } - local nodes, light_values, param2s = {}, params.light_values and {}, {} - local vm_nodes, vm_light_values, vm_param2s = voxelmanip:get_data(), light_values and voxelmanip:get_light_data(), voxelmanip:get_param2_data() - local node_names, node_ids = {}, {} - local i = 0 - for index in voxelarea:iterp(pos_min, pos_max) do - if nodes[index] == minetest.CONTENT_UNKNOWN or nodes[index] == minetest.CONTENT_IGNORE then - error("unknown or ignore node at " .. minetest.pos_to_string(voxelarea:position(index))) - end - local name = minetest.get_name_from_content_id(vm_nodes[index]) - local id = node_ids[name] - if not id then - table.insert(node_names, name) - id = #node_names - node_ids[name] = id - end - i = i + 1 - nodes[i] = id - if params.light_values then - light_values[i] = vm_light_values[index] - end - param2s[i] = vm_param2s[index] - end - local metas - if params.metas or params.metas == nil then - metas = {} - for _, pos in ipairs(minetest.find_nodes_with_meta(pos_min, pos_max)) do - local meta = minetest.get_meta(pos):to_table() - if next(meta.fields) ~= nil or next(meta.inventory) ~= nil then - local relative = vector.subtract(pos, pos_min) - metas[((relative.z * size.y) + relative.y) * size.x + relative.x] = meta - end - end - end - return schematic.setmetatable({ - size = size, - node_names = node_names, - nodes = nodes, - light_values = light_values, - param2s = param2s, - metas = metas, - }) -end - -function schematic:write_to_voxelmanip(voxelmanip, pos_min) - local size = self.size - local pos_max = vector.subtract(vector.add(pos_min, size), 1) -- `pos_max` is inclusive - local emin, emax = voxelmanip:read_from_map(pos_min, pos_max) - local voxelarea = VoxelArea:new{ MinEdge = emin, MaxEdge = emax } - local nodes, light_values, param2s, metas = self.nodes, self.light_values, self.param2s, self.metas - local vm_nodes, vm_lights, vm_param2s = voxelmanip:get_data(), light_values and voxelmanip:get_light_data(), voxelmanip:get_param2_data() - for _, pos in ipairs(minetest.find_nodes_with_meta(pos_min, pos_max)) do - -- Clear all metadata. Due to an engine bug, nodes will actually have empty metadata. - minetest.get_meta(pos):from_table{} - end - local content_ids = {} - for index, name in ipairs(self.node_names) do - content_ids[index] = assert(minetest.get_content_id(name), ("unknown node %q"):format(name)) - end - local i = 0 - for index in voxelarea:iterp(pos_min, pos_max) do - i = i + 1 - vm_nodes[index] = content_ids[nodes[i]] - if light_values then - vm_lights[index] = light_values[i] - end - vm_param2s[index] = param2s[i] - end - voxelmanip:set_data(vm_nodes) - if light_values then - voxelmanip:set_light_data(vm_lights) - end - voxelmanip:set_param2_data(vm_param2s) - if metas then - for index, meta in pairs(metas) do - local floored = math.floor(index / size.x) - local relative = { - x = index % size.x, - y = floored % size.y, - z = math.floor(floored / size.y) - } - minetest.get_meta(vector.add(relative, pos_min)):from_table(meta) - end - end -end - -function schematic:place(pos_min) - local pos_max = vector.subtract(vector.add(pos_min, self.size), 1) -- `pos_max` is inclusive - local voxelmanip = minetest.get_voxel_manip(pos_min, pos_max) - self:write_to_voxelmanip(voxelmanip, pos_min) - voxelmanip:write_to_map(not self.light_values) - return voxelmanip -end - -local function table_to_byte_string(tab) - if not tab then return end - return table.concat(modlib.table.map(tab, string.char)) -end - -local function write_bluon(self, stream) - local metas = modlib.table.copy(self.metas) - for _, meta in pairs(metas) do - for _, list in pairs(meta.inventory) do - for index, stack in pairs(list) do - list[index] = stack:to_string() - end - end - end - modlib.bluon:write({ - size = self.size, - node_names = self.node_names, - nodes = self.nodes, - light_values = table_to_byte_string(self.light_values), - param2s = table_to_byte_string(self.param2s), - metas = metas, - }, stream) -end - -function schematic:write_bluon(path) - local file = io.open(path, "wb") - -- Header, short for "ModLib Bluon Schematic" - file:write"MLBS" - write_bluon(self, file) - file:close() -end - -local function byte_string_to_table(self, field) - local byte_string = self[field] - if not byte_string then return end - local tab = {} - for i = 1, #byte_string do - tab[i] = byte_string:byte(i) - end - self[field] = tab -end - -local function read_bluon(file) - local self = modlib.bluon:read(file) - assert(not file:read(1), "expected EOF") - for _, meta in pairs(self.metas) do - for _, list in pairs(meta.inventory) do - for index, itemstring in pairs(list) do - assert(type(itemstring) == "string") - list[index] = ItemStack(itemstring) - end - end - end - byte_string_to_table(self, "light_values") - byte_string_to_table(self, "param2s") - return self -end - -function schematic.read_bluon(path) - local file = io.open(path, "rb") - assert(file:read(4) == "MLBS", "not a modlib bluon schematic") - return schematic.setmetatable(read_bluon(file)) -end - -function schematic:write_zlib_bluon(path, compression) - local file = io.open(path, "wb") - -- Header, short for "ModLib Zlib-compressed-bluon Schematic" - file:write"MLZS" - local rope = modlib.table.rope{} - write_bluon(self, rope) - local text = rope:to_text() - file:write(minetest.compress(text, "deflate", compression or 9)) - file:close() -end - -function schematic.read_zlib_bluon(path) - local file = io.open(path, "rb") - assert(file:read(4) == "MLZS", "not a modlib zlib compressed bluon schematic") - return schematic.setmetatable(read_bluon(modlib.text.inputstream(minetest.decompress(file:read"*a", "deflate")))) -end - -return schematic diff --git a/mods/modlib/minetest/texmod.lua b/mods/modlib/minetest/texmod.lua deleted file mode 100644 index 6553b6e6..00000000 --- a/mods/modlib/minetest/texmod.lua +++ /dev/null @@ -1,30 +0,0 @@ --- Texture Modifier representation for building, parsing and stringifying texture modifiers according to --- https://github.com/minetest/minetest_docs/blob/master/doc/texture_modifiers.adoc - -local function component(component_name, ...) - return assert(loadfile(modlib.mod.get_resource(modlib.modname, "minetest", "texmod", component_name .. ".lua")))(...) -end - -local texmod, metatable = component"dsl" -local methods = metatable.__index -methods.write = component"write" -texmod.read = component("read", texmod) -methods.calc_dims = component"calc_dims" -methods.gen_tex = component"gen_tex" - -function metatable:__tostring() - local rope = {} - self:write(function(str) rope[#rope+1] = str end) - return table.concat(rope) -end - -function texmod.read_string(str, warn --[[function(warn_str)]]) - local i = 0 - return texmod.read(function() - i = i + 1 - if i > #str then return end - return str:sub(i, i) - end, warn) -end - -return texmod diff --git a/mods/modlib/minetest/texmod/calc_dims.lua b/mods/modlib/minetest/texmod/calc_dims.lua deleted file mode 100644 index 292e937c..00000000 --- a/mods/modlib/minetest/texmod/calc_dims.lua +++ /dev/null @@ -1,97 +0,0 @@ -local cd = {} - -local function calc_dims(self, get_file_dims) - return assert(cd[self.type])(self, get_file_dims) -end - -function cd:file(d) - return d(self.filename) -end - -do - local function base_dim(self, get_dims) return calc_dims(self.base, get_dims) end - cd.opacity = base_dim - cd.invert = base_dim - cd.brighten = base_dim - cd.noalpha = base_dim - cd.makealpha = base_dim - cd.lowpart = base_dim - cd.mask = base_dim - cd.multiply = base_dim - cd.colorize = base_dim - cd.colorizehsl = base_dim - cd.hsl = base_dim - cd.screen = base_dim - cd.contrast = base_dim -end - -do - local function wh(self) return self.w, self.h end - cd.resize = wh - cd.combine = wh -end - -function cd:fill(get_dims) - if self.base then return calc_dims(self.base, get_dims) end - return self.w, self.h -end - -do - local function upscale_to_higher_res(self, get_dims) - local base_w, base_h = calc_dims(self.base, get_dims) - local over_w, over_h = calc_dims(self.over, get_dims) - if base_w * base_h > over_w * over_h then - return base_w, base_h - end - return over_w, over_h - end - cd.blit = upscale_to_higher_res - cd.hardlight = upscale_to_higher_res -end - -function cd:transform(get_dims) - if self.rotation_deg % 180 ~= 0 then - local base_w, base_h = calc_dims(self.base, get_dims) - return base_h, base_w - end - return calc_dims(self.base, get_dims) -end - -do - local math_clamp = modlib.math.clamp - local function next_pow_of_2(x) - -- I don't want to use a naive 2^ceil(log(x)/log(2)) due to possible float precision issues. - local m, e = math.frexp(x) -- x = _*2^e, _ in [0.5, 1) - if m == 0.5 then e = e - 1 end -- x = 2^(e-1) - return math.ldexp(1, e) -- 2^e, premature optimization here we go - end - function cd:inventorycube(get_dims) - local top_w, top_h = calc_dims(self.top, get_dims) - local left_w, left_h = calc_dims(self.left, get_dims) - local right_w, right_h = calc_dims(self.right, get_dims) - local d = math_clamp(next_pow_of_2(math.max(top_w, top_h, left_w, left_h, right_w, right_h)), 2, 64) - return d, d - end -end - -do - local function frame_dims(self, get_dims) - local base_w, base_h = calc_dims(self.base, get_dims) - return base_w, math.floor(base_h / self.framecount) - end - cd.verticalframe = frame_dims - cd.crack = frame_dims - cd.cracko = frame_dims -end - -function cd:sheet(get_dims) - local base_w, base_h = calc_dims(self.base, get_dims) - return math.floor(base_w / self.w), math.floor(base_h / self.h) -end - -function cd:png() - local png = modlib.minetest.decode_png(modlib.text.inputstream(self.data)) - return png.width, png.height -end - -return calc_dims diff --git a/mods/modlib/minetest/texmod/dsl.lua b/mods/modlib/minetest/texmod/dsl.lua deleted file mode 100644 index beee4345..00000000 --- a/mods/modlib/minetest/texmod/dsl.lua +++ /dev/null @@ -1,422 +0,0 @@ -local colorspec = modlib.minetest.colorspec - -local texmod = {} -local mod = {} -local metatable = {__index = mod} - -local function new(self) - return setmetatable(self, metatable) -end - --- `texmod{...}` may be used to create texture modifiers, bypassing the checks -setmetatable(texmod, {__call = new}) - --- Constructors / "generators" - -function texmod.file(filename) - -- See `TEXTURENAME_ALLOWED_CHARS` in Minetest (`src/network/networkprotocol.h`) - assert(not filename:find"[^%w_.-]", "invalid characters in file name") - return new{ - type = "file", - filename = filename - } -end - -function texmod.png(data) - assert(type(data) == "string") - return new{ - type = "png", - data = data - } -end - -function texmod.combine(w, h, blits) - assert(w % 1 == 0 and w > 0) - assert(h % 1 == 0 and h > 0) - for _, blit in ipairs(blits) do - assert(blit.x % 1 == 0) - assert(blit.y % 1 == 0) - assert(blit.texture) - end - return new{ - type = "combine", - w = w, - h = h, - blits = blits - } -end - -function texmod.inventorycube(top, left, right) - return new{ - type = "inventorycube", - top = top, - left = left, - right = right - } -end - --- As a base generator, `fill` ignores `x` and `y`. Leave them as `nil`. -function texmod.fill(w, h, color) - assert(w % 1 == 0 and w > 0) - assert(h % 1 == 0 and h > 0) - return new{ - type = "fill", - w = w, - h = h, - color = colorspec.from_any(color) - } -end - --- Methods / "modifiers" - -local function assert_int_range(num, min, max) - assert(num % 1 == 0 and num >= min and num <= max) -end - --- As a modifier, `fill` takes `x` and `y` -function mod:fill(w, h, x, y, color) - assert(w % 1 == 0 and w > 0) - assert(h % 1 == 0 and h > 0) - assert(x % 1 == 0 and x >= 0) - assert(y % 1 == 0 and y >= 0) - return new{ - type = "fill", - base = self, - w = w, - h = h, - x = x, - y = y, - color = colorspec.from_any(color) - } -end - --- This is the real "overlay", associated with `^`. -function mod:blit(overlay) - return new{ - type = "blit", - base = self, - over = overlay - } -end - -function mod:brighten() - return new{ - type = "brighten", - base = self, - } -end - -function mod:noalpha() - return new{ - type = "noalpha", - base = self - } -end - -function mod:resize(w, h) - assert(w % 1 == 0 and w > 0) - assert(h % 1 == 0 and h > 0) - return new{ - type = "resize", - base = self, - w = w, - h = h, - } -end - -local function assert_uint8(num) - assert_int_range(num, 0, 0xFF) -end - -function mod:makealpha(r, g, b) - assert_uint8(r); assert_uint8(g); assert_uint8(b) - return new{ - type = "makealpha", - base = self, - r = r, g = g, b = b - } -end - -function mod:opacity(ratio) - assert_uint8(ratio) - return new{ - type = "opacity", - base = self, - ratio = ratio - } -end - -local function tobool(val) - return not not val -end - -function mod:invert(channels --[[set with keys "r", "g", "b", "a"]]) - return new{ - type = "invert", - base = self, - r = tobool(channels.r), - g = tobool(channels.g), - b = tobool(channels.b), - a = tobool(channels.a) - } -end - -function mod:flip(flip_axis --[["x" or "y"]]) - return self:transform(assert( - (flip_axis == "x" and "fx") - or (flip_axis == "y" and "fy") - or (not flip_axis and "i"))) -end - -function mod:rotate(deg) - assert(deg % 90 == 0) - deg = deg % 360 - return self:transform(("r%d"):format(deg)) -end - --- D4 group transformations (see https://proofwiki.org/wiki/Definition:Dihedral_Group_D4), --- represented using indices into a table of matrices --- TODO (...) try to come up with a more elegant solution -do - -- Matrix multiplication for composition: First applies a, then b <=> b * a - local function mat_2x2_compose(a, b) - local a_1_1, a_1_2, a_2_1, a_2_2 = unpack(a) - local b_1_1, b_1_2, b_2_1, b_2_2 = unpack(b) - return { - a_1_1 * b_1_1 + a_2_1 * b_1_2, a_1_2 * b_1_1 + a_2_2 * b_1_2; - a_1_1 * b_2_1 + a_2_1 * b_2_2, a_1_2 * b_2_1 + a_2_2 * b_2_2 - } - end - local r90 ={ - 0, -1; - 1, 0 - } - local fx = { - -1, 0; - 0, 1 - } - local fy = { - 1, 0; - 0, -1 - } - local r180 = mat_2x2_compose(r90, r90) - local r270 = mat_2x2_compose(r180, r90) - local fxr90 = mat_2x2_compose(fx, r90) - local fyr90 = mat_2x2_compose(fy, r90) - local transform_mats = {[0] = {1, 0; 0, 1}, r90, r180, r270, fx, fxr90, fy, fyr90} - local transform_idx_by_name = {i = 0, r90 = 1, r180 = 2, r270 = 3, fx = 4, fxr90 = 5, fy = 6, fyr90 = 7} - -- Lookup tables for getting the flipped axis / rotation angle - local flip_by_idx = { - [4] = "x", - [5] = "x", - [6] = "y", - [7] = "y", - } - local rot_by_idx = { - [1] = 90, - [2] = 180, - [3] = 270, - [5] = 90, - [7] = 90, - } - local idx_by_mat_2x2 = {} - local function transform_idx(mat) - -- note: assumes mat[i] in {-1, 0, 1} - return mat[1] + 3*(mat[2] + 3*(mat[3] + 3*mat[4])) - end - for i = 0, 7 do - idx_by_mat_2x2[transform_idx(transform_mats[i])] = i - end - -- Compute a multiplication table - local composition_idx = {} - local function ij_idx(i, j) - return i*8 + j - end - for i = 0, 7 do - for j = 0, 7 do - composition_idx[ij_idx(i, j)] = assert(idx_by_mat_2x2[ - transform_idx(mat_2x2_compose(transform_mats[i], transform_mats[j]))]) - end - end - function mod:transform(...) - if select("#", ...) == 0 then return self end - local idx = ... - if type(idx) == "string" then - idx = assert(transform_idx_by_name[idx:lower()]) - end - local base = self - if self.type == "transform" then - -- Merge with a `^[transform` base image - assert(transform_mats[idx]) - base = self.base - idx = composition_idx[ij_idx(self.idx, idx)] - end - assert(transform_mats[idx]) - if idx == 0 then return base end -- identity - return new{ - type = "transform", - base = base, - idx = idx, - -- Redundantly store this information for convenience. Do not modify! - flip_axis = flip_by_idx[idx], - rotation_deg = rot_by_idx[idx] or 0, - }:transform(select(2, ...)) - end -end - -function mod:verticalframe(framecount, frame) - assert(framecount >= 1) - assert(frame >= 0) - return new{ - type = "verticalframe", - base = self, - framecount = framecount, - frame = frame - } -end - -local function crack(self, name, ...) - local tilecount, framecount, frame - if select("#", ...) == 2 then - tilecount, framecount, frame = 1, ... - else - assert(select("#", ...) == 3, "invalid number of arguments") - tilecount, framecount, frame = ... - end - assert(tilecount >= 1) - assert(framecount >= 1) - assert(frame >= 0) - return new{ - type = name, - base = self, - tilecount = tilecount, - framecount = framecount, - frame = frame - } -end - -function mod:crack(...) - return crack(self, "crack", ...) -end - -function mod:cracko(...) - return crack(self, "cracko", ...) -end -mod.crack_with_opacity = mod.cracko - -function mod:sheet(w, h, x, y) - assert(w % 1 == 0 and w >= 1) - assert(h % 1 == 0 and h >= 1) - assert(x % 1 == 0 and x >= 0) - assert(y % 1 == 0 and y >= 0) - return new{ - type = "sheet", - base = self, - w = w, - h = h, - x = x, - y = y - } -end - -function mod:screen(color) - return new{ - type = "screen", - base = self, - color = colorspec.from_any(color), - } -end - -function mod:multiply(color) - return new{ - type = "multiply", - base = self, - color = colorspec.from_any(color) - } -end - -function mod:colorize(color, ratio) - color = colorspec.from_any(color) - if ratio == "alpha" then - assert(color.alpha or 0xFF == 0xFF) - else - ratio = ratio or color.alpha or 0xFF - assert_uint8(ratio) - if color.alpha == ratio then - ratio = nil - end - end - return new{ - type = "colorize", - base = self, - color = color, - ratio = ratio - } -end - -local function hsl(type, s_def, s_max, l_def) - return function(self, h, s, l) - s, l = s or s_def, l or l_def - assert_int_range(h, -180, 180) - assert_int_range(s, 0, s_max) - assert_int_range(l, -100, 100) - return new{ - type = type, - base = self, - hue = h, - saturation = s, - lightness = l, - } - end -end - -mod.colorizehsl = hsl("colorizehsl", 50, 100, 0) -mod.hsl = hsl("hsl", 0, math.huge, 0) - -function mod:contrast(contrast, brightness) - brightness = brightness or 0 - assert_int_range(contrast, -127, 127) - assert_int_range(brightness, -127, 127) - return new{ - type = "contrast", - base = self, - contrast = contrast, - brightness = brightness, - } -end - -function mod:mask(mask_texmod) - return new{ - type = "mask", - base = self, - _mask = mask_texmod - } -end - -function mod:hardlight(overlay) - return new{ - type = "hardlight", - base = self, - over = overlay - } -end - --- Overlay *blend*. --- This was unfortunately named `[overlay` in Minetest, --- and so is named `:overlay` for consistency. ---! Do not confuse this with the simple `^` used for blitting -function mod:overlay(overlay) - return overlay:hardlight(self) -end - -function mod:lowpart(percent, overlay) - assert(percent % 1 == 0 and percent >= 0 and percent <= 100) - return new{ - type = "lowpart", - base = self, - percent = percent, - over = overlay - } -end - -return texmod, metatable diff --git a/mods/modlib/minetest/texmod/gen_tex.lua b/mods/modlib/minetest/texmod/gen_tex.lua deleted file mode 100644 index acedd0b6..00000000 --- a/mods/modlib/minetest/texmod/gen_tex.lua +++ /dev/null @@ -1,190 +0,0 @@ -local tex = modlib.tex - -local paths = modlib.minetest.media.paths -local function read_png(fname) - if fname == "blank.png" then return tex.new{w=1,h=1,0} end - return tex.read_png(assert(paths[fname])) -end - -local gt = {} - --- TODO colorizehsl, hsl, contrast --- TODO (...) inventorycube; this is nontrivial. - -function gt:file() - return read_png(self.filename) -end - -function gt:opacity() - local t = self.base:gen_tex() - t:opacity(self.ratio / 255) - return t -end - -function gt:invert() - local t = self.base:gen_tex() - t:invert(self.r, self.g, self.b, self.a) - return t -end - -function gt:brighten() - local t = self.base:gen_tex() - t:brighten() - return t -end - -function gt:noalpha() - local t = self.base:gen_tex() - t:noalpha() - return t -end - -function gt:makealpha() - local t = self.base:gen_tex() - t:makealpha(self.r, self.g, self.b) - return t -end - -function gt:multiply() - local c = self.color - local t = self.base:gen_tex() - t:multiply_rgb(c.r, c.g, c.b) - return t -end - -function gt:screen() - local c = self.color - local t = self.base:gen_tex() - t:screen_blend_rgb(c.r, c.g, c.b) - return t -end - -function gt:colorize() - local c = self.color - local t = self.base:gen_tex() - t:colorize(c.r, c.g, c.b, self.ratio) - return t -end - -local function resized_to_larger(a, b) - if a.w * a.h > b.w * b.h then - b = b:resized(a.w, a.h) - else - a = a:resized(b.w, b.h) - end - return a, b -end - -function gt:mask() - local a, b = resized_to_larger(self.base:gen_tex(), self._mask:gen_tex()) - a:band(b) - return a -end - -function gt:lowpart() - local t = self.base:gen_tex() - local over = self.over:gen_tex() - local lowpart_h = math.ceil(self.percent/100 * over.h) -- TODO (?) ceil or floor - if lowpart_h > 0 then - t, over = resized_to_larger(t, over) - local y = over.h - lowpart_h + 1 - over:crop(1, y, over.w, over.h) - t:blit(1, y, over) - end - return t -end - -function gt:resize() - return self.base:gen_tex():resized(self.w, self.h) -end - -function gt:combine() - local t = tex.filled(self.w, self.h, 0) - for _, blt in ipairs(self.blits) do - t:blit(blt.x + 1, blt.y + 1, blt.texture:gen_tex()) - end - return t -end - -function gt:fill() - if self.base then - return self.base:gen_tex():fill(self.w, self.h, self.x, self.y, self.color:to_number()) - end - return tex.filled(self.w, self.h, self.color:to_number()) -end - -function gt:blit() - local t, o = resized_to_larger(self.base:gen_tex(), self.over:gen_tex()) - t:blit(1, 1, o) - return t -end - -function gt:hardlight() - local t, o = resized_to_larger(self.base:gen_tex(), self.over:gen_tex()) - t:hardlight_blend(o) - return t -end - --- TODO (...?) optimize this -function gt:transform() - local t = self.base:gen_tex() - if self.flip_axis == "x" then - t:flip_x() - elseif self.flip_axis == "y" then - t:flip_y() - end - -- TODO implement counterclockwise rotations to get rid of this hack - for _ = 1, 360 - self.rotation_deg / 90 do - t = t:rotated_90() - end - return t -end - -local frame = function(t, frame, framecount) - local fh = math.floor(t.h / framecount) - t:crop(1, frame * fh + 1, t.w, (frame + 1) * fh) -end - -local crack = function(self, o) - local crack = read_png"crack_anylength.png" - frame(crack, self.frame, math.floor(crack.h / crack.w)) - local t = self.base:gen_tex() - local tile_w, tile_h = math.floor(t.w / self.tilecount), math.floor(t.h / self.framecount) - crack = crack:resized(tile_w, tile_h) - for ty = 1, t.h, tile_h do - for tx = 1, t.w, tile_w do - t[o and "blito" or "blit"](t, tx, ty, crack) - end - end - return t -end - -function gt:crack() - return crack(self, false) -end - -function gt:cracko() - return crack(self, true) -end - -function gt:verticalframe() - local t = self.base:gen_tex() - frame(t, self.frame, self.framecount) - return t -end - -function gt:sheet() - local t = self.base:gen_tex() - local tw, th = math.floor(t.w / self.w), math.floor(t.h / self.h) - local x, y = self.x, self.y - t:crop(x * tw + 1, y * th + 1, (x + 1) * tw, (y + 1) * th) - return t -end - -function gt:png() - return tex.read_png_string(self.data) -end - -return function(self) - return assert(gt[self.type])(self) -end diff --git a/mods/modlib/minetest/texmod/read.lua b/mods/modlib/minetest/texmod/read.lua deleted file mode 100644 index b4ceedb4..00000000 --- a/mods/modlib/minetest/texmod/read.lua +++ /dev/null @@ -1,429 +0,0 @@ -local texmod = ... -local colorspec = modlib.minetest.colorspec - --- Generator readers - -local gr = {} - -function gr.png(r) - r:expect":" - local base64 = r:match_str"[a-zA-Z0-9+/=]" - return assert(minetest.decode_base64(base64), "invalid base64") -end - -function gr.inventorycube(r) - local top = r:invcubeside() - local left = r:invcubeside() - local right = r:invcubeside() - return top, left, right -end - -function gr.combine(r) - r:expect":" - local w = r:int() - r:expect"x" - local h = r:int() - local blits = {} - while r:match":" do - if r.eof then break end -- we can just end with `:`, right? - local x = r:int() - r:expect"," - local y = r:int() - r:expect"=" - table.insert(blits, {x = x, y = y, texture = r:subtexp()}) - end - return w, h, blits -end - -function gr.fill(r) - r:expect":" - local w = r:int() - r:expect"x" - local h = r:int() - r:expect":" - -- Be strict(er than Minetest): Do not accept x, y for a base - local color = r:colorspec() - return w, h, color -end - --- Parameter readers - -local pr = {} - -function pr.fill(r) - r:expect":" - local w = r:int() - r:expect"x" - local h = r:int() - r:expect":" - if assert(r:peek(), "unexpected eof"):find"%d" then - local x = r:int() - r:expect"," - local y = r:int() - r:expect":" - local color = r:colorspec() - return w, h, x, y, color - end - local color = r:colorspec() - return w, h, color -end - -function pr.brighten() end - -function pr.noalpha() end - -function pr.resize(r) - r:expect":" - local w = r:int() - r:expect"x" - local h = r:int() - return w, h -end - -function pr.makealpha(r) - r:expect":" - local red = r:int() - r:expect"," - local green = r:int() - r:expect"," - local blue = r:int() - return red, green, blue -end - -function pr.opacity(r) - r:expect":" - local ratio = r:int() - return ratio -end - -function pr.invert(r) - r:expect":" - local channels = {} - while true do - local c = r:match_charset"[rgba]" - if not c then break end - channels[c] = true - end - return channels -end - -do - function pr.transform(r) - if r:match_charset"[iI]" then - return pr.transform(r) - end - local idx = r:match_charset"[0-7]" - if idx then - return tonumber(idx), pr.transform(r) - end - if r:match_charset"[fF]" then - local flip_axis = assert(r:match_charset"[xXyY]", "axis expected") - return "f" .. flip_axis, pr.transform(r) - end - if r:match_charset"[rR]" then - local deg = r:match_str"%d" - -- Be strict here: Minetest won't recognize other ways to write these numbers (or other numbers) - assert(deg == "90" or deg == "180" or deg == "270") - return ("r%d"):format(deg), pr.transform(r) - end - -- return nothing, we're done - end -end - -function pr.verticalframe(r) - r:expect":" - local framecount = r:int() - r:expect":" - local frame = r:int() - return framecount, frame -end - -function pr.crack(r) - r:expect":" - local framecount = r:int() - r:expect":" - local frame = r:int() - if r:match":" then - return framecount, frame, r:int() - end - return framecount, frame -end -pr.cracko = pr.crack - -function pr.sheet(r) - r:expect":" - local w = r:int() - r:expect"x" - local h = r:int() - r:expect":" - local x = r:int() - r:expect"," - local y = r:int() - return w, h, x, y -end - -function pr.multiply(r) - r:expect":" - return r:colorspec() -end -pr.screen = pr.multiply - -function pr.colorize(r) - r:expect":" - local color = r:colorspec() - if not r:match":" then - return color - end - if not r:match"a" then - return color, r:int() - end - for c in ("lpha"):gmatch"." do - r:expect(c) - end - return color, "alpha" -end - -function pr.colorizehsl(r) - r:expect":" - local hue = r:int() - if not r:match":" then - return hue - end - local saturation = r:int() - if not r:match":" then - return hue, saturation - end - local lightness = r:int() - return hue, saturation, lightness -end -pr.hsl = pr.colorizehsl - -function pr.contrast(r) - r:expect":" - local contrast = r:int() - if not r:match":" then - return contrast - end - local brightness = r:int() - return contrast, brightness -end - -function pr.overlay(r) - r:expect":" - return r:subtexp() -end - -function pr.hardlight(r) - r:expect":" - return r:subtexp() -end - -function pr.mask(r) - r:expect":" - return r:subtexp() -end - -function pr.lowpart(r) - r:expect":" - local percent = r:int() - assert(percent) - r:expect":" - return percent, r:subtexp() -end - --- Build a prefix tree of parameter readers to greedily match the longest texture modifier prefix; --- just matching `%a+` and looking it up in a table --- doesn't work since `[transform` may be followed by a lowercase transform name --- TODO (?...) consolidate with `modlib.trie` -local texmod_reader_trie = {} -for _, readers in pairs{pr, gr} do - for type in pairs(readers) do - local subtrie = texmod_reader_trie - for char in type:gmatch"." do - subtrie[char] = subtrie[char] or {} - subtrie = subtrie[char] - end - subtrie.type = type - end -end - --- Reader methods. We use `r` instead of the `self` "sugar" for consistency (and to save us some typing). -local rm = {} - -function rm.peek(r, parenthesized) - if r.eof then return end - local expected_escapes = 0 - if r.level > 0 then - -- Premature optimization my beloved (this is `2^(level-1)`) - expected_escapes = math.ldexp(0.5, r.level) - end - if r.character:match"[&^:]" then -- "special" characters - these need to be escaped - if r.escapes == expected_escapes then - return r.character - elseif parenthesized and r.character == "^" and r.escapes < expected_escapes then - -- Special handling for `^` inside `(...)`: This is undocumented behavior but works in Minetest - r.warn"parenthesized caret (`^`) with too few escapes" - return r.character - end - elseif r.escapes <= expected_escapes then - return r.character - end if r.escapes >= 2*expected_escapes then - return "\\" - end -end -function rm.popchar(r) - assert(not r.eof, "unexpected eof") - r.escapes = 0 - while true do - r.character = r:read_char() - if r.character ~= "\\" then break end - r.escapes = r.escapes + 1 - end - if r.character == nil then - assert(r.escapes == 0, "end of texmod expected") - r.eof = true - end -end -function rm.pop(r) - local expected_escapes = 0 - if r.level > 0 then - -- Premature optimization my beloved (this is `2^(level-1)`) - expected_escapes = math.ldexp(0.5, r.level) - end - if r.escapes > 0 and r.escapes >= 2*expected_escapes then - r.escapes = r.escapes - 2*expected_escapes - return - end - return r:popchar() -end -function rm.match(r, char) - if r:peek() == char then - r:pop() - return true - end -end -function rm.expect(r, char) - if not r:match(char) then - error(("%q expected"):format(char)) - end -end -function rm.hat(r, parenthesized) - if r:peek(parenthesized) == (r.invcube and "&" or "^") then - r:pop() - return true - end -end -function rm.match_charset(r, set) - local char = r:peek() - if char and char:match(set) then - r:pop() - return char - end -end -function rm.match_str(r, set) - local c = r:match_charset(set) - if not c then - error(("character in %s expected"):format(set)) - end - local t = {c} - while true do - c = r:match_charset(set) - if not c then break end - table.insert(t, c) - end - return table.concat(t) -end -function rm.int(r) - local sign = 1 - if r:match"-" then sign = -1 end - return sign * tonumber(r:match_str"%d") -end -function rm.fname(r) - -- This is overly permissive, as is Minetest; - -- we just allow arbitrary characters up until a character which may terminate the name. - -- Inside an inventorycube, `&` also terminates names. - -- Note that the constructor will however - unlike Minetest - perform validation. - -- We could leverage the knowledge of the allowed charset here already, - -- but that might lead to more confusing error messages. - return r:match_str(r.invcube and "[^:^&){]" or "[^:^){]") -end -function rm.subtexp(r) - r.level = r.level + 1 - local res = r:texp() - r.level = r.level - 1 - return res -end -function rm.invcubeside(r) - assert(not r.invcube, "can't nest inventorycube") - r.invcube = true - assert(r:match"{", "'{' expected") - local res = r:texp() - r.invcube = false - return res -end -function rm.basexp(r) - if r:match"(" then - local res = r:texp(true) - r:expect")" - return res - end - if r:match"[" then - local type = r:match_str"%a" - local gen_reader = gr[type] - if not gen_reader then - error("invalid texture modifier: " .. type) - end - return texmod[type](gen_reader(r)) - end - return texmod.file(r:fname()) -end -function rm.colorspec(r) - -- Leave exact validation up to colorspec, only do a rough greedy charset matching - return assert(colorspec.from_string(r:match_str"[#%x%a]")) -end -function rm.texp(r, parenthesized) - local base = r:basexp() -- TODO (?) make optional - warn about omitting the base - while r:hat(parenthesized) do - if r:match"[" then - local reader_subtrie = texmod_reader_trie - while true do - local next_subtrie = reader_subtrie[r:peek()] - if next_subtrie then - reader_subtrie = next_subtrie - r:pop() - else - break - end - end - local type = assert(reader_subtrie.type, "invalid texture modifier") - local param_reader, gen_reader = pr[type], gr[type] - assert(param_reader or gen_reader) - if param_reader then - -- Note: It is important that this takes precedence to properly handle `[fill` - base = base[type](base, param_reader(r)) - elseif gen_reader then - base = base:blit(texmod[type](gen_reader(r))) - end - -- TODO (?...) we could consume leftover parameters here to be as lax as Minetest - else - base = base:blit(r:basexp()) - end - end - return base -end - -local mt = {__index = rm} -return function(read_char, warn --[[function(str)]]) - local r = setmetatable({ - level = 0, - invcube = false, - parenthesized = false, - eof = false, - read_char = read_char, - warn = warn or error, - }, mt) - r:popchar() - local res = r:texp(false) - assert(r.eof, "eof expected") - return res -end diff --git a/mods/modlib/minetest/texmod/write.lua b/mods/modlib/minetest/texmod/write.lua deleted file mode 100644 index c99f56d5..00000000 --- a/mods/modlib/minetest/texmod/write.lua +++ /dev/null @@ -1,181 +0,0 @@ -local pw = {} -- parameter writers: `[type] = func(self, write)` - -function pw:png(w) - w.colon(); w.str(minetest.encode_base64(self.data)) -end - -function pw:combine(w) - w.colon(); w.int(self.w); w.str"x"; w.str(self.h) - for _, blit in ipairs(self.blits) do - w.colon() - w.int(blit.x); w.str","; w.int(blit.y); w.str"=" - w.esctex(blit.texture) - end -end - -function pw:inventorycube(w) - assert(not w.inventorycube, "[inventorycube may not be nested") - local function write_side(side) - w.str"{" - w.inventorycube = true - w.tex(self[side]) - w.inventorycube = false - end - write_side"top" - write_side"left" - write_side"right" -end - --- Handles both the generator & the modifier -function pw:fill(w) - w.colon(); w.int(self.w); w.str"x"; w.int(self.h) - if self.base then - w.colon(); w.int(self.x); w.str","; w.int(self.y) - end - w.colon(); w.str(self.color:to_string()) -end - --- No parameters to write -pw.brighten = modlib.func.no_op -pw.noalpha = modlib.func.no_op - -function pw:resize(w) - w.colon(); w.int(self.w); w.str"x"; w.int(self.h) -end - -function pw:makealpha(w) - w.colon(); w.int(self.r); w.str","; w.int(self.g); w.str","; w.int(self.b) -end - -function pw:opacity(w) - w.colon(); w.int(self.ratio) -end - -function pw:invert(w) - w.colon() - if self.r then w.str"r" end - if self.g then w.str"g" end - if self.b then w.str"b" end - if self.a then w.str"a" end -end - -function pw:transform(w) - w.int(self.idx) -end - -function pw:verticalframe(w) - w.colon(); w.int(self.framecount); w.colon(); w.int(self.frame) -end - -function pw:crack(w) - w.colon(); w.int(self.tilecount); w.colon(); w.int(self.framecount); w.colon(); w.int(self.frame) -end - -pw.cracko = pw.crack - -function pw:sheet(w) - w.colon(); w.int(self.w); w.str"x"; w.int(self.h); w.colon(); w.int(self.x); w.str","; w.int(self.y) -end - -function pw:screen(w) - w.colon() - w.str(self.color:to_string()) -end - -function pw:multiply(w) - w.colon() - w.str(self.color:to_string()) -end - -function pw:colorize(w) - w.colon() - w.str(self.color:to_string()) - if self.ratio then - w.colon() - if self.ratio == "alpha" then - w.str"alpha" - else - w.int(self.ratio) - end - end -end - -function pw:colorizehsl(w) - w.colon(); w.int(self.hue); w.colon(); w.int(self.saturation); w.colon(); w.int(self.lightness) -end -pw.hsl = pw.colorizehsl - -function pw:contrast(w) - w.colon(); w.int(self.contrast); w.colon(); w.int(self.brightness) -end - --- We don't have to handle `[overlay`; the DSL normalizes everything to `[hardlight` -function pw:hardlight(w) - w.colon(); w.esctex(self.over) -end - -function pw:mask(w) - w.colon(); w.esctex(self._mask) -end - -function pw:lowpart(w) - w.colon(); w.int(self.percent); w.colon(); w.esctex(self.over) -end - --- Set of "non-modifiers" which do not modify a base image -local non_modifiers = {file = true, png = true, combine = true, inventorycube = true} - -return function(self, write_str) - -- We could use a metatable here, but it wouldn't really be worth it; - -- it would save us instantiating a handful of closures at the cost of constant `__index` events - -- and having to constantly pass `self`, increasing code complexity - local w = {} - w.inventorycube = false - w.level = 0 - w.str = write_str - function w.esc() - if w.level == 0 then return end - w.str(("\\"):rep(math.ldexp(0.5, w.level))) - end - function w.hat() - -- Note: We technically do not need to escape `&` inside an [inventorycube which is nested inside [combine, - -- but we do it anyways for good practice and since we have to escape `&` inside [combine inside [inventorycube. - w.esc() - w.str(w.inventorycube and "&" or "^") - end - function w.colon() - w.esc(); w.str":" - end - function w.int(int) - w.str(("%d"):format(int)) - end - function w.tex(tex) - if tex.type == "file" then - w.str(tex.filename) - return - end - if tex.base then - w.tex(tex.base) - w.hat() - end - if tex.type == "blit" then -- simply `^` - if non_modifiers[tex.over.type] then - w.tex(tex.over) - else - -- Make sure the modifier is first applied to its base image - -- and only after this overlaid on top of `tex.base` - w.str"("; w.tex(tex.over); w.str")" - end - else - w.str"[" - w.str(tex.type) - pw[tex.type](tex, w) - end - end - function w.esctex(tex) - w.level = w.level + 1 - w.tex(tex) - w.level = w.level - 1 - end - w.tex(self) -end diff --git a/mods/modlib/minetest/wielditem_change.lua b/mods/modlib/minetest/wielditem_change.lua deleted file mode 100644 index 30d57938..00000000 --- a/mods/modlib/minetest/wielditem_change.lua +++ /dev/null @@ -1,66 +0,0 @@ --- Localize globals -local minetest, modlib, pairs, table = minetest, modlib, pairs, table - --- Set environment -local _ENV = ... -setfenv(1, _ENV) - -players = {} - -registered_on_wielditem_changes = {function(...) - local _, previous_item, _, item = ... - if previous_item then - ((previous_item:get_definition()._modlib or {}).un_wield or modlib.func.no_op)(...) - end - if item then - ((item:get_definition()._modlib or {}).on_wield or modlib.func.no_op)(...) - end -end} - ---+ Registers an on_wielditem_change callback: function(player, previous_item, previous_index, item) ---+ Will be called once with player, nil, index, item on join -register_on_wielditem_change = modlib.func.curry(table.insert, registered_on_wielditem_changes) - -local function register_callbacks() - minetest.register_on_joinplayer(function(player) - local item, index = player:get_wielded_item(), player:get_wield_index() - players[player:get_player_name()] = { - wield = { - item = item, - index = index - } - } - modlib.table.icall(registered_on_wielditem_changes, player, nil, index, item) - end) - minetest.register_on_leaveplayer(function(player) - players[player:get_player_name()] = nil - end) -end - --- Other on_joinplayer / on_leaveplayer callbacks should execute first -if minetest.get_current_modname() then - -- Loaded during load time, register callbacks after load time - minetest.register_on_mods_loaded(register_callbacks) -else - -- Loaded after load time, register callbacks immediately - register_callbacks() -end - --- TODO export -local function itemstack_equals(a, b) - return a:get_name() == b:get_name() and a:get_count() == b:get_count() and a:get_wear() == b:get_wear() and a:get_meta():equals(b:get_meta()) -end - -minetest.register_globalstep(function() - for _, player in pairs(minetest.get_connected_players()) do - local item, index = player:get_wielded_item(), player:get_wield_index() - local playerdata = players[player:get_player_name()] - if not playerdata then return end - local previous_item, previous_index = playerdata.wield.item, playerdata.wield.index - if not (itemstack_equals(item, previous_item) and index == previous_index) then - playerdata.wield.item = item - playerdata.wield.index = index - modlib.table.icall(registered_on_wielditem_changes, player, previous_item, previous_index, item) - end - end -end) \ No newline at end of file diff --git a/mods/modlib/mod.conf b/mods/modlib/mod.conf deleted file mode 100644 index 3f6fba1c..00000000 --- a/mods/modlib/mod.conf +++ /dev/null @@ -1,5 +0,0 @@ -name = modlib -title = Modding Library -description = Multipurpose Minetest Modding Library -author = LMD aka appguru(eu) -optional_depends = dbg, strictest diff --git a/mods/modlib/persistence.lua b/mods/modlib/persistence.lua deleted file mode 100644 index e57ebafc..00000000 --- a/mods/modlib/persistence.lua +++ /dev/null @@ -1,23 +0,0 @@ -local require = ... or require --- TODO consider moving serializers in this namespace -local function load(module_name) - return assert(loadfile(modlib.mod.get_resource(modlib.modname, "persistence", module_name .. ".lua"))) -end -return setmetatable({}, {__index = function(self, module_name) - if module_name == "lua_log_file" then - local module = load(module_name)() - self[module_name] = module - return module - end - if module_name == "sqlite3" then - local func = load(module_name) - local module = function(sqlite3) - if sqlite3 then - return func(sqlite3) - end - return func(require"lsqlite3") - end - self[module_name] = module - return module - end -end}) \ No newline at end of file diff --git a/mods/modlib/persistence/lua_log_file.lua b/mods/modlib/persistence/lua_log_file.lua deleted file mode 100644 index 94b740c6..00000000 --- a/mods/modlib/persistence/lua_log_file.lua +++ /dev/null @@ -1,194 +0,0 @@ --- Localize globals -local assert, error, io, loadfile, math, minetest, modlib, pairs, setfenv, setmetatable, type - = assert, error, io, loadfile, math, minetest, modlib, pairs, setfenv, setmetatable, type - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - --- Default value -reference_strings = true - --- Note: keys may not be marked as weak references: garbage collected log files wouldn't close the file: --- The `__gc` metamethod doesn't work for tables in Lua 5.1; a hack using `newproxy` would be needed --- See https://stackoverflow.com/questions/27426704/lua-5-1-workaround-for-gc-metamethod-for-tables) --- Therefore, :close() must be called on log files to remove them from the `files` table -local files = {} -local metatable = {__index = _ENV} -_ENV.metatable = metatable - -function new(file_path, root, reference_strings) - local self = setmetatable({ - file_path = assert(file_path), - root = root, - reference_strings = reference_strings - }, metatable) - if minetest then - files[self] = true - end - return self -end - -local function set_references(self, table) - -- Weak table keys to allow the collection of dead reference tables - -- TODO garbage collect strings in the references table - self.references = setmetatable(table, {__mode = "k"}) -end - -function load(self) - -- Bytecode is blocked by the engine - local read = assert(loadfile(self.file_path)) - -- math.huge is serialized to inf - local env = {inf = math.huge} - setfenv(read, env) - read() - env.R = env.R or {{}} - local reference_count = #env.R - for ref in pairs(env.R) do - if ref > reference_count then - -- Ensure reference count always has the value of the largest reference - -- in case of "holes" (nil values) in the reference list - reference_count = ref - end - end - self.reference_count = reference_count - self.root = env.R[1] - set_references(self, {}) -end - -function open(self) - self.file = io.open(self.file_path, "a+") -end - -function init(self) - if modlib.file.exists(self.file_path) then - self:load() - self:_rewrite() - self:open() - return - end - self:open() - self:_write() -end - -function log(self, statement) - self.file:write(statement) - self.file:write"\n" -end - -function flush(self) - self.file:flush() -end - -function close(self) - self.file:close() - self.file = nil - files[self] = nil -end - -if minetest then - minetest.register_on_shutdown(function() - for self in pairs(files) do - self.file:close() - end - end) -end - -local function _dump(self, value, is_key) - if value == nil then - return "nil" - end - if value == true then - return "true" - end - if value == false then - return "false" - end - if value ~= value then - -- nan - return "0/0" - end - local _type = type(value) - if _type == "number" then - return ("%.17g"):format(value) - end - local reference = self.references[value] - if reference then - return "R[" .. reference .."]" - end - reference = self.reference_count + 1 - local key = "R[" .. reference .."]" - local function create_reference() - self.reference_count = reference - self.references[value] = reference - end - if _type == "string" then - local reference_strings = self.reference_strings - if is_key and ((not reference_strings) or value:len() <= key:len()) and modlib.text.is_identifier(value) then - -- Short key - return value, true - end - local formatted = ("%q"):format(value) - if (not reference_strings) or formatted:len() <= key:len() then - -- Short string - return formatted - end - -- Use reference - create_reference() - self:log(key .. "=" .. formatted) - elseif _type == "table" then - -- Tables always need a reference before they are traversed to prevent infinite recursion - create_reference() - -- TODO traverse tables to determine whether this is actually needed - self:log(key .. "={}") - for k, v in pairs(value) do - local dumped, short = _dump(self, k, true) - self:log(key .. (short and ("." .. dumped) or ("[" .. dumped .. "]")) .. "=" .. _dump(self, v)) - end - else - error("unsupported type: " .. _type) - end - return key -end - -function set(self, table, key, value) - if not self.references[table] then - error"orphan table" - end - if table[key] == value then - -- No change - return - end - table[key] = value - table = _dump(self, table) - local key, short_key = _dump(self, key, true) - self:log(table .. (short_key and ("." .. key) or ("[" .. key .. "]")) .. "=" .. _dump(self, value)) -end - -function set_root(self, key, value) - return self:set(self.root, key, value) -end - -function _write(self) - set_references(self, {}) - self.reference_count = 0 - self:log"R={}" - _dump(self, self.root) -end - -function _rewrite(self) - self.file = io.open(self.file_path, "w+") - self:_write() - self.file:close() -end - -function rewrite(self) - if self.file then - self.file:close() - end - self:_rewrite() - self:open() -end - --- Export environment -return _ENV \ No newline at end of file diff --git a/mods/modlib/persistence/sqlite3.lua b/mods/modlib/persistence/sqlite3.lua deleted file mode 100644 index 60c484e0..00000000 --- a/mods/modlib/persistence/sqlite3.lua +++ /dev/null @@ -1,318 +0,0 @@ -local assert, error, math_huge, modlib, minetest, setmetatable, type, table_insert, table_sort, pairs, ipairs - = assert, error, math.huge, modlib, minetest, setmetatable, type, table.insert, table.sort, pairs, ipairs - -local sqlite3 = ... - ---[[ - Currently uses reference counting to immediately delete tables which aren't reachable from the root table anymore, which has two issues: - 1. Deletion might trigger a large deletion chain - TODO defer deletion, clean up unused tables on startup, delete & iterate tables partially - 2. Reference counting is unable to handle cycles. `:collectgarbage()` implements a tracing "stop-the-world" garbage collector which handles cycles. - TODO take advantage of Lua's garbage collection by keeping a bunch of "twin" objects in a weak structure using proxies (Lua 5.1) or the __gc metamethod (Lua 5.2) - See https://wiki.c2.com/?ReferenceCountingCanHandleCycles, https://www.memorymanagement.org/mmref/recycle.html#mmref-recycle and https://wiki.c2.com/?GenerationalGarbageCollectio - Weak tables are of no use here, as we need to be notified when a reference is dropped -]] - -local ptab = {} -- SQLite3-backed implementation for a persistent Lua table ("ptab") -local metatable = {__index = ptab} -ptab.metatable = metatable - --- Note: keys may not be marked as weak references: wouldn't close the database: see persistence/lua_log_file.lua -local databases = {} - -local types = { - boolean = 1, - number = 2, - string = 3, - table = 4 -} - -local function increment_highest_table_id(self) - self.highest_table_id = self.highest_table_id + 1 - if self.highest_table_id > 2^50 then - -- IDs are approaching double precision limit (52 bits mantissa), defragment them - self:defragment_ids() - end - return self.highest_table_id -end - -function ptab.new(file_path, root) - return setmetatable({ - database = sqlite3.open(file_path), - root = root - }, metatable) -end - -function ptab.setmetatable(self) - assert(self.database and self.root) - return setmetatable(self, metatable) -end - -local set - -local function add_table(self, table) - if type(table) ~= "table" then return end - if self.counts[table] then - self.counts[table] = self.counts[table] + 1 - return - end - self.table_ids[table] = increment_highest_table_id(self) - self.counts[table] = 1 - for k, v in pairs(table) do - set(self, table, k, v) - end -end - -local decrement_reference_count - -local function delete_table(self, table) - local id = assert(self.table_ids[table]) - self.table_ids[table] = nil - self.counts[table] = nil - for k, v in pairs(table) do - decrement_reference_count(self, k) - decrement_reference_count(self, v) - end - local statement = self._prepared.delete_table - statement:bind(1, id) - statement:step() - statement:reset() -end - -function decrement_reference_count(self, table) - if type(table) ~= "table" then return end - local count = self.counts[table] - if not count then return end - count = count - 1 - if count == 0 then return delete_table(self, table) end - self.counts[table] = count -end - -function set(self, table, key, value) - local deletion = value == nil - if not deletion then - add_table(self, key) - add_table(self, value) - end - local previous_value = table[key] - if type(previous_value) == "table" then - decrement_reference_count(self, previous_value) - end - if deletion and type(key) == "table" then - decrement_reference_count(self, key) - end - local statement = self._prepared[deletion and "delete" or "insert"] - local function bind_type_and_content(n, value) - local type_ = type(value) - statement:bind(n, assert(types[type_])) - if type_ == "boolean" then - statement:bind(n + 1, value and 1 or 0) - elseif type_ == "number" then - if value ~= value then - statement:bind(n + 1, "nan") - elseif value == math_huge then - statement:bind(n + 1, "inf") - elseif value == -math_huge then - statement:bind(n + 1, "-inf") - else - statement:bind(n + 1, value) - end - elseif type_ == "string" then - -- Use bind_blob instead of bind as Lua strings are effectively byte strings - statement:bind_blob(n + 1, value) - elseif type_ == "table" then - statement:bind(n + 1, self.table_ids[value]) - end - end - statement:bind(1, assert(self.table_ids[table])) - bind_type_and_content(2, key) - if not deletion then - bind_type_and_content(4, value) - end - statement:step() - statement:reset() -end - -local function exec(self, sql) - if self.database:exec(sql) ~= sqlite3.OK then - error(self.database:errmsg()) - end -end - -function ptab:init() - local database = self.database - local function prepare(sql) - local stmt = database:prepare(sql) - if not stmt then error(database:errmsg()) end - return stmt - end - exec(self, [[ -CREATE TABLE IF NOT EXISTS table_entries ( - table_id INTEGER NOT NULL, - key_type INTEGER NOT NULL, - key BLOB NOT NULL, - value_type INTEGER NOT NULL, - value BLOB NOT NULL, - PRIMARY KEY (table_id, key_type, key) -)]]) - self._prepared = { - insert = prepare"INSERT OR REPLACE INTO table_entries(table_id, key_type, key, value_type, value) VALUES (?, ?, ?, ?, ?)", - delete = prepare"DELETE FROM table_entries WHERE table_id = ? AND key_type = ? AND key = ?", - delete_table = prepare"DELETE FROM table_entries WHERE table_id = ?", - update = { - id = prepare"UPDATE table_entries SET table_id = ? WHERE table_id = ?", - keys = prepare("UPDATE table_entries SET key = ? WHERE key_type = " .. types.table .. " AND key = ?"), - values = prepare("UPDATE table_entries SET value = ? WHERE value_type = " .. types.table .. " AND value = ?") - } - } - -- Default value - self.highest_table_id = 0 - for id in self.database:urows"SELECT MAX(table_id) FROM table_entries" do - -- Gets a single value - self.highest_table_id = id - end - increment_highest_table_id(self) - local tables = {} - local counts = {} - self.counts = counts - local function get_value(type_, content) - if type_ == types.boolean then - if content == 0 then return false end - if content == 1 then return true end - error("invalid boolean value: " .. content) - end - if type_ == types.number then - if content == "nan" then - return 0/0 - end - if content == "inf" then - return math_huge - end - if content == "-inf" then - return -math_huge - end - assert(type(content) == "number") - return content - end - if type_ == types.string then - assert(type(content) == "string") - return content - end - if type_ == types.table then - -- Table reference - tables[content] = tables[content] or {} - counts[content] = counts[content] or 1 - return tables[content] - end - -- Null is unused - error("unsupported type: " .. type_) - end - -- Order by key_content to retrieve list parts in the correct order, making it easier for Lua - for table_id, key_type, key, value_type, value in self.database:urows"SELECT * FROM table_entries ORDER BY table_id, key_type, key" do - local table = tables[table_id] or {} - counts[table] = counts[table] or 1 - table[get_value(key_type, key)] = get_value(value_type, value) - tables[table_id] = table - end - if tables[1] then - self.root = tables[1] - counts[self.root] = counts[self.root] + 1 - self.table_ids = modlib.table.flip(tables) - self:collectgarbage() - else - self.highest_table_id = 0 - self.table_ids = {} - add_table(self, self.root) - end - databases[self] = true -end - -function ptab:rewrite() - exec(self, "BEGIN EXCLUSIVE TRANSACTION") - exec(self, "DELETE FROM table_entries") - self.highest_table_id = 0 - self.table_ids = {} - self.counts = {} - add_table(self, self.root) - exec(self, "COMMIT TRANSACTION") -end - -function ptab:set(table, key, value) - exec(self, "BEGIN EXCLUSIVE TRANSACTION") - local previous_value = table[key] - if previous_value == value then - -- no change - return - end - set(self, table, key, value) - table[key] = value - exec(self, "COMMIT TRANSACTION") -end - -function ptab:set_root(key, value) - return self:set(self.root, key, value) -end - -function ptab:collectgarbage() - local marked = {} - local function mark(table) - if type(table) ~= "table" or marked[table] then return end - marked[table] = true - for k, v in pairs(table) do - mark(k) - mark(v) - end - end - mark(self.root) - for table in pairs(self.table_ids) do - if not marked[table] then - delete_table(self, table) - end - end -end - -function ptab:defragment_ids() - local ids = {} - for _, id in pairs(self.table_ids) do - table_insert(ids, id) - end - table_sort(ids) - local update = self._prepared.update - local tables = modlib.table.flip(self.table_ids) - for new_id, old_id in ipairs(ids) do - for _, stmt in pairs(update) do - stmt:bind_values(new_id, old_id) - stmt:step() - stmt:reset() - end - self.table_ids[tables[old_id]] = new_id - end - self.highest_table_id = #ids -end - -local function finalize_statements(table) - for _, stmt in pairs(table) do - if type(stmt) == "table" then - finalize_statements(stmt) - else - local errcode = stmt:finalize() - assert(errcode == sqlite3.OK, errcode) - end - end -end - -function ptab:close() - finalize_statements(self._prepared) - self.database:close() - databases[self] = nil -end - -if minetest then - minetest.register_on_shutdown(function() - for self in pairs(databases) do - self:close() - end - end) -end - -return ptab \ No newline at end of file diff --git a/mods/modlib/quaternion.lua b/mods/modlib/quaternion.lua deleted file mode 100644 index de28bd7e..00000000 --- a/mods/modlib/quaternion.lua +++ /dev/null @@ -1,165 +0,0 @@ --- Localize globals -local math, modlib, pairs, unpack, vector = math, modlib, pairs, unpack, vector - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - --- TODO OOP, extend vector - -function from_euler_rotation(rotation) - rotation = vector.divide(rotation, 2) - local cos = vector.apply(rotation, math.cos) - local sin = vector.apply(rotation, math.sin) - return { - cos.z * sin.x * cos.y + sin.z * cos.x * sin.y, - cos.z * cos.x * sin.y - sin.z * sin.x * cos.y, - sin.z * cos.x * cos.y - cos.z * sin.x * sin.y, - cos.z * cos.x * cos.y + sin.z * sin.x * sin.y - } -end - -function from_euler_rotation_deg(rotation) - return from_euler_rotation(vector.apply(rotation, math.rad)) -end - -function multiply(self, other) - local X, Y, Z, W = unpack(self) - return normalize{ - (other[4] * X) + (other[1] * W) + (other[2] * Z) - (other[3] * Y); - (other[4] * Y) + (other[2] * W) + (other[3] * X) - (other[1] * Z); - (other[4] * Z) + (other[3] * W) + (other[1] * Y) - (other[2] * X); - (other[4] * W) - (other[1] * X) - (other[2] * Y) - (other[3] * Z); - } -end - -function compose(self, other) - return multiply(other, self) -end - -function len(self) - return (self[1] ^ 2 + self[2] ^ 2 + self[3] ^ 2 + self[4] ^ 2) ^ 0.5 -end - -function normalize(self) - local l = len(self) - local res = {} - for key, value in pairs(self) do - res[key] = value / l - end - return res -end - -function conjugate(self) - return { - -self[1], - -self[2], - -self[3], - self[4] - } -end - -function inverse(self) - -- TODO this is just a fancy normalization *of the conjungate*, - -- which for rotations is the inverse - return modlib.vector.divide_scalar(conjugate(self), self[1] ^ 2 + self[2] ^ 2 + self[3] ^ 2 + self[4] ^ 2) -end - -function negate(self) - for key, value in pairs(self) do - self[key] = -value - end -end - -function dot(self, other) - return self[1] * other[1] + self[2] * other[2] + self[3] * other[3] + self[4] * other[4] -end - ---: self normalized quaternion ---: other normalized quaternion -function slerp(self, other, ratio) - local d = dot(self, other) - if d < 0 then - d = -d - negate(other) - end - -- Threshold beyond which linear interpolation is used - if d > 1 - 1e-10 then - return modlib.vector.interpolate(self, other, ratio) - end - local theta_0 = math.acos(d) - local theta = theta_0 * ratio - local sin_theta = math.sin(theta) - local sin_theta_0 = math.sin(theta_0) - local s_1 = sin_theta / sin_theta_0 - local s_0 = math.cos(theta) - d * s_1 - return modlib.vector.add(modlib.vector.multiply_scalar(self, s_0), modlib.vector.multiply_scalar(other, s_1)) -end - ---> axis, angle -function to_axis_angle(self) - local axis = modlib.vector.new{self[1], self[2], self[3]} - local len = axis:length() - -- HACK invert axis for correct rotation in Minetest - return len == 0 and axis or axis:divide_scalar(-len), 2 * math.atan2(len, self[4]) -end - -function to_euler_rotation_rad(self) - local rotation = {} - - local sinr_cosp = 2 * (self[4] * self[1] + self[2] * self[3]) - local cosr_cosp = 1 - 2 * (self[1] ^ 2 + self[2] ^ 2) - rotation.x = math.atan2(sinr_cosp, cosr_cosp) - - local sinp = 2 * (self[4] * self[2] - self[3] * self[1]) - if sinp <= -1 then - rotation.y = -math.pi/2 - elseif sinp >= 1 then - rotation.y = math.pi/2 - else - rotation.y = math.asin(sinp) - end - - local siny_cosp = 2 * (self[4] * self[3] + self[1] * self[2]) - local cosy_cosp = 1 - 2 * (self[2] ^ 2 + self[3] ^ 2) - rotation.z = math.atan2(siny_cosp, cosy_cosp) - - return rotation -end - --- TODO rename this to to_euler_rotation_deg eventually (breaking change) ---> {x = pitch, y = yaw, z = roll} euler rotation in degrees -function to_euler_rotation(self) - return vector.apply(to_euler_rotation_rad(self), math.deg) -end - --- See https://github.com/zaki/irrlicht/blob/master/include/quaternion.h#L652 -function to_euler_rotation_irrlicht(self) - local x, y, z, w = unpack(self) - local test = 2 * (y * w - x * z) - - local rot - if math.abs(test - 1) <= 1e-6 then - rot = { - z = -2 * math.atan2(x, w), - x = 0, - y = math.pi/2 - } - elseif math.abs(test + 1) <= 1e-6 then - rot = { - z = 2 * math.atan2(x, w), - x = 0, - y = math.pi/-2 - } - else - rot = { - z = math.atan2(2 * (x * y + z * w), x ^ 2 - y ^ 2 - z ^ 2 + w ^ 2), - x = math.atan2(2 * (y * z + x * w), -x ^ 2 - y ^ 2 + z ^ 2 + w ^ 2), - y = math.asin(math.min(math.max(test, -1), 1)) - } - end - return vector.apply(rot, math.deg) -end - --- Export environment -return _ENV diff --git a/mods/modlib/schema.lua b/mods/modlib/schema.lua deleted file mode 100644 index e23ca943..00000000 --- a/mods/modlib/schema.lua +++ /dev/null @@ -1,331 +0,0 @@ --- Localize globals -local assert, error, ipairs, math, minetest, modlib, pairs, setmetatable, table, tonumber, tostring, type = assert, error, ipairs, math, minetest, modlib, pairs, setmetatable, table, tonumber, tostring, type - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - -local metatable = {__index = _ENV} - -function new(def) - -- TODO type inference, sanity checking etc. - return setmetatable(def, metatable) -end - -local function field_name_to_title(name) - local title = modlib.text.split(name, "_") - title[1] = modlib.text.upper_first(title[1]) - return table.concat(title, " ") -end - -function generate_settingtypes(self) - local typ = self.type - local settingtype, type_args - self.title = self.title or field_name_to_title(self.name) - self._level = self._level or 0 - local default = self.default - if typ == "boolean" then - settingtype = "bool" - default = default and "true" or "false" - elseif typ == "string" then - settingtype = "string" - if self.values then - local values = {} - for value in pairs(self.values) do - if value:find"," then - values = nil - break - end - table.insert(values, value) - end - if values then - settingtype = "enum" - type_args = table.concat(values, ",") - end - end - elseif typ == "number" then - settingtype = self.int and "int" or "float" - if self.range and (self.range.min or self.range.max) then - -- TODO handle exclusive min/max - type_args = (self.int and "%d %d" or "%f %f"):format(self.range.min or (2 ^ -30), self.range.max or (2 ^ 30)) - end - elseif typ == "table" then - local settings = {} - if self._level > 0 then - -- HACK: Minetest automatically adds the modname - -- TODO simple names (not modname.field.other_field) - settings = {"[" .. ("*"):rep(self._level - 1) .. self.name .. "]"} - end - local function setting(key, value_scheme) - key = tostring(key) - assert(not key:find("[=%.%s]")) - value_scheme.name = self.name .. "." .. key - value_scheme.title = value_scheme.title or self.title .. " " .. field_name_to_title(key) - value_scheme._level = self._level + 1 - table.insert(settings, generate_settingtypes(value_scheme)) - end - local keys = {} - for key in pairs(self.entries or {}) do - table.insert(keys, key) - end - table.sort(keys, function(key, other_key) - -- Force leaves before subtrees to prevent them from being accidentally graphically treated as part of the subtree - local is_subtree = self.entries[key].type == "table" - local other_is_subtree = self.entries[other_key].type == "table" - if is_subtree ~= other_is_subtree then - return not is_subtree - end - return key < other_key - end) - for _, key in ipairs(keys) do - setting(key, self.entries[key]) - end - return table.concat(settings, "\n\n") - end - if not typ then - return "" - end - local description = self.description - -- TODO extend description by range etc.? - -- TODO enum etc. support - if description then - if type(description) ~= "table" then - description = {description} - end - description = "# " .. table.concat(description, "\n# ") .. "\n" - else - description = "" - end - return description .. self.name .. " (" .. self.title .. ") " .. settingtype .. " " .. (default or "") .. (type_args and (" " .. type_args) or "") -end - -function generate_markdown(self) - -- TODO address redundancies - local function description(lines) - local description = self.description - if description then - if type(description) ~= "table" then - table.insert(lines, description) - else - modlib.table.append(lines, description) - end - end - end - local typ = self.type - self.title = self.title or field_name_to_title(self._md_name) - self._md_level = self._md_level or 1 - if typ == "table" then - local settings = {} - description(settings) - -- TODO generate Markdown for key/value-checks - local function setting(key, value_scheme) - value_scheme._md_name = key - value_scheme.title = value_scheme.title or self.title .. " " .. field_name_to_title(key) - value_scheme._md_level = self._md_level + 1 - table.insert(settings, table.concat(modlib.table.repetition("#", self._md_level)) .. " `" .. key .. "`") - table.insert(settings, "") - table.insert(settings, generate_markdown(value_scheme)) - table.insert(settings, "") - end - local keys = {} - for key in pairs(self.entries or {}) do - table.insert(keys, key) - end - table.sort(keys) - for _, key in ipairs(keys) do - setting(key, self.entries[key]) - end - return table.concat(settings, "\n") - end - if not typ then - return "" - end - local lines = {} - description(lines) - local function line(text) - table.insert(lines, "* " .. text) - end - table.insert(lines, "") - line("Type: " .. self.type) - if self.default ~= nil then - line("Default: `" .. tostring(self.default) .. "`") - end - if self.int then - line"Integer" - elseif self.list then - line"List" - end - if self.infinity then - line"Infinities allowed" - end - if self.nan then - line"Not-a-Number (NaN) allowed" - end - if self.range then - if self.range.min then - line(">= `" .. self.range.min .. "`") - elseif self.range.min_exclusive then - line("> `" .. self.range.min_exclusive .. "`") - end - if self.range.max then - line("<= `" .. self.range.max .. "`") - elseif self.range.max_exclusive then - line("< `" .. self.range.max_exclusive .. "`") - end - end - if self.values then - line("Possible values:") - for value in pairs(self.values) do - table.insert(lines, " * " .. value) - end - end - return table.concat(lines, "\n") -end - -function settingtypes(self) - self.settingtypes = self.settingtypes or generate_settingtypes(self) - return self.settingtypes -end - -function load(self, override, params) - local converted - if params.convert_strings and type(override) == "string" then - converted = true - if self.type == "boolean" then - if override == "true" then - override = true - elseif override == "false" then - override = false - end - elseif self.type == "number" then - override = tonumber(override) - else - converted = false - end - end - if override == nil and not converted then - if self.type == "table" and self.default == nil then - override = {} - else - return self.default - end - end - local _error = error - local function format_error(typ, ...) - if typ == "type" then - return "mismatched type: expected " .. self.type ..", got " .. type(override) .. (converted and " (converted)" or "") - end - if typ == "range" then - local conditions = {} - local function push(condition, bound) - if self.range[bound] then - table.insert(conditions, " " .. condition .. " " .. minetest.write_json(self.range[bound])) - end - end - push(">", "min_exclusive") - push(">=", "min") - push("<", "max_exclusive") - push("<=", "max") - return "out of range: expected value" .. table.concat(conditions, " and") - end - if typ == "int" then - return "expected integer" - end - if typ == "infinity" then - return "expected no infinity" - end - if typ == "nan" then - return "expected no nan" - end - if typ == "required" then - local key = ... - return "required field " .. minetest.write_json(key) .. " missing" - end - if typ == "additional" then - local key = ... - return "superfluous field " .. minetest.write_json(key) - end - if typ == "list" then - return "not a list" - end - if typ == "values" then - return "expected one of " .. minetest.write_json(modlib.table.keys(self.values)) .. ", got " .. minetest.write_json(override) - end - _error("unknown error type") - end - local function error(type, ...) - if params.error_message then - local formatted = format_error(type, ...) - _error("Invalid value: " .. (self.name and (self.name .. ": ") or "") .. formatted) - end - _error{ - type = type, - self = self, - override = override, - converted = converted - } - end - local function assert(value, ...) - if not value then - error(...) - end - return value - end - assert(self.type == type(override), "type") - if self.type == "number" or self.type == "string" then - if self.range then - if self.range.min then - assert(self.range.min <= override, "range") - elseif self.range.min_exclusive then - assert(self.range.min_exclusive < override, "range") - end - if self.range.max then - assert(self.range.max >= override, "range") - elseif self.range.max_exclusive then - assert(self.range.max_exclusive > override, "range") - end - end - if self.type == "number" then - assert((not self.int) or (override % 1 == 0), "int") - assert(self.infinity or math.abs(override) ~= math.huge, "infinity") - assert(self.nan or override == override, "nan") - end - elseif self.type == "table" then - if self.keys then - for key, value in pairs(override) do - override[load(self.keys, key, params)], override[key] = value, nil - end - end - if self.values then - for key, value in pairs(override) do - override[key] = load(self.values, value, params) - end - end - if self.entries then - for key, schema in pairs(self.entries) do - if schema.required and override[key] == nil then - error("required", key) - end - override[key] = load(schema, override[key], params) - end - if self.additional == false then - for key in pairs(override) do - if self.entries[key] == nil then - error("additional", key) - end - end - end - end - assert((not self.list) or modlib.table.count(override) == #override, "list") - end - -- Apply the values check only for primitive types where table indexing is by value; - -- the `values` field has a different meaning for tables (constraint all values must fulfill) - if self.type ~= "table" then - assert((not self.values) or self.values[override], "values") - end - if self.func then self.func(override) end - return override -end - --- Export environment -return _ENV \ No newline at end of file diff --git a/mods/modlib/table.lua b/mods/modlib/table.lua deleted file mode 100644 index 14cc307b..00000000 --- a/mods/modlib/table.lua +++ /dev/null @@ -1,889 +0,0 @@ --- Localize globals -local assert, ipairs, math, next, pairs, rawget, rawset, getmetatable, setmetatable, select, string, table, type - = assert, ipairs, math, next, pairs, rawget, rawset, getmetatable, setmetatable, select, string, table, type - -local lt = modlib.func.lt - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - --- Empty table -empty = {} - --- Table helpers - -function from_iterator(...) - local table = {} - for key, value in ... do - table[key] = value - end - return table -end - -function default(table, value) - return setmetatable(table, { - __index = function() - return value - end, - }) -end - -function map_index(table, func) - local mapping_metatable = { - __index = function(table, key) - return rawget(table, func(key)) - end, - __newindex = function(table, key, value) - rawset(table, func(key), value) - end - } - return setmetatable(table, mapping_metatable) -end - -function set_case_insensitive_index(table) - return map_index(table, string.lower) -end - ---+ nilget(a, "b", "c") == a?.b?.c -function nilget(value, ...) - local n = select("#", ...) - for i = 1, n do - if value == nil then return nil end - value = value[select(i, ...)] - end - return value -end - -deepget = nilget - ---+ `deepset(a, "b", "c", d)` is the same as `a.b = a.b ?? {}; a.b.c = d` -function deepset(table, ...) - local n = select("#", ...) - for i = 1, n - 2 do - local key = select(i, ...) - local parent = table - table = parent[key] - if table == nil then - table = {} - parent[key] = table - end - end - table[select(n - 1, ...)] = select(n, ...) -end - --- Fisher-Yates -function shuffle(table) - for index = 1, #table - 1 do - local index_2 = math.random(index, #table) - table[index], table[index_2] = table[index_2], table[index] - end - return table -end - -local rope_metatable = {__index = { - write = function(self, text) - table.insert(self, text) - end, - to_text = function(self) - return table.concat(self) - end -}} ---> rope with simple metatable (:write(text) and :to_text()) -function rope(table) - return setmetatable(table or {}, rope_metatable) -end - -local rope_len_metatable = {__index = { - write = function(self, text) - self.len = self.len + text:len() - end -}} ---> rope for determining length of text supporting `:write(text)` and `.len` to get the length of written text -function rope_len(len) - return setmetatable({len = len or 0}, rope_len_metatable) -end - -function is_circular(table) - assert(type(table) == "table") - local known = {} - local function _is_circular(value) - if type(value) ~= "table" then - return false - end - if known[value] then - return true - end - known[value] = true - for key, value in pairs(value) do - if _is_circular(key) or _is_circular(value) then - return true - end - end - end - return _is_circular(table) -end - ---+ Simple table equality check. Stack overflow if tables are too deep or circular. ---+ Use `is_circular(table)` to check whether a table is circular. ---> Equality of noncircular tables if `table` and `other_table` are tables ---> `table == other_table` else -function equals_noncircular(table, other_table) - local is_equal = table == other_table - if is_equal or type(table) ~= "table" or type(other_table) ~= "table" then - return is_equal - end - if #table ~= #other_table then - return false - end - local table_keys = {} - for key, value in pairs(table) do - local value_2 = other_table[key] - if not equals_noncircular(value, value_2) then - if type(key) == "table" then - table_keys[key] = value - else - return false - end - end - end - for other_key, other_value in pairs(other_table) do - if type(other_key) == "table" then - local found - for table, value in pairs(table_keys) do - if equals_noncircular(other_key, table) and equals_noncircular(other_value, value) then - table_keys[table] = nil - found = true - break - end - end - if not found then - return false - end - else - if table[other_key] == nil then - return false - end - end - end - return true -end - -equals = equals_noncircular - ---+ Table equality check properly handling circular tables - tables are equal as long as they provide equal key/value-pairs ---> Table content equality if `table` and `other_table` are tables ---> `table == other_table` else -function equals_content(table, other_table) - local equal_tables = {} - local function _equals(table, other_equal_table) - local function set_equal_tables(value) - equal_tables[table] = equal_tables[table] or {} - equal_tables[table][other_equal_table] = value - return value - end - local is_equal = table == other_equal_table - if is_equal or type(table) ~= "table" or type(other_equal_table) ~= "table" then - return is_equal - end - if #table ~= #other_equal_table then - return set_equal_tables(false) - end - local lookup_equal = (equal_tables[table] or {})[other_equal_table] - if lookup_equal ~= nil then - return lookup_equal - end - -- Premise - set_equal_tables(true) - local table_keys = {} - for key, value in pairs(table) do - local other_value = other_equal_table[key] - if not _equals(value, other_value) then - if type(key) == "table" then - table_keys[key] = value - else - return set_equal_tables(false) - end - end - end - for other_key, other_value in pairs(other_equal_table) do - if type(other_key) == "table" then - local found = false - for table_key, value in pairs(table_keys) do - if _equals(table_key, other_key) and _equals(value, other_value) then - table_keys[table_key] = nil - found = true - -- Breaking is fine as per transitivity - break - end - end - if not found then - return set_equal_tables(false) - end - else - if table[other_key] == nil then - return set_equal_tables(false) - end - end - end - return true - end - return _equals(table, other_table) -end - ---+ Table equality check: content has to be equal, relations between tables as well ---+ The only difference may be in the memory addresses ("identities") of the (sub)tables ---+ Performance may suffer if the tables contain table keys ---+ equals(table, copy(table)) is true ---> equality (same tables after table reference substitution) of circular tables if `table` and `other_table` are tables ---> `table == other_table` else -function equals_references(table, other_table) - local function _equals(table, other_table, equal_refs) - if equal_refs[table] then - return equal_refs[table] == other_table - end - local is_equal = table == other_table - -- this check could be omitted if table key equality is being checked - if type(table) ~= "table" or type(other_table) ~= "table" then - return is_equal - end - if is_equal then - equal_refs[table] = other_table - return true - end - -- Premise: table = other table - equal_refs[table] = other_table - local table_keys = {} - for key, value in pairs(table) do - if type(key) == "table" then - table_keys[key] = value - else - local other_value = other_table[key] - if not _equals(value, other_value, equal_refs) then - return false - end - end - end - local other_table_keys = {} - for other_key, other_value in pairs(other_table) do - if type(other_key) == "table" then - other_table_keys[other_key] = other_value - elseif table[other_key] == nil then - return false - end - end - local function _next(current_key, equal_refs, available_keys) - local key, value = next(table_keys, current_key) - if key == nil then - return true - end - for other_key, other_value in pairs(other_table_keys) do - local copy_equal_refs = shallowcopy(equal_refs) - if _equals(key, other_key, copy_equal_refs) and _equals(value, other_value, copy_equal_refs) then - local copy_available_keys = shallowcopy(available_keys) - copy_available_keys[other_key] = nil - if _next(key, copy_equal_refs, copy_available_keys) then - return true - end - end - end - return false - end - return _next(nil, equal_refs, other_table_keys) - end - return _equals(table, other_table, {}) -end - --- Supports circular tables; does not support table keys ---> `true` if a mapping of references exists, `false` otherwise -function same(a, b) - local same = {} - local function is_same(a, b) - if type(a) ~= "table" or type(b) ~= "table" then - return a == b - end - if same[a] or same[b] then - return same[a] == b and same[b] == a - end - if a == b then - return true - end - - same[a], same[b] = b, a - local count = 0 - for k, v in pairs(a) do - count = count + 1 - assert(type(k) ~= "table", "table keys not supported") - if not is_same(v, b[k], same) then - return false - end - end - for _ in pairs(b) do - count = count - 1 - if count < 0 then - return false - end - end - return true - end - return is_same(a, b) -end - -function shallowcopy( - table -- table to copy - , strip_metatables -- whether to strip metatables; falsy by default; metatables are not copied -) - if type(table) ~= "table" then - return table - end - - local copy = {} - if not strip_metatables then - setmetatable(copy, getmetatable(table)) - end - for key, value in pairs(table) do - copy[key] = value - end - return copy -end - -function deepcopy_tree( - table -- table; may not contain circular references; cross references will be copied multiple times - , strip_metatables -- whether to strip metatables; falsy by default; metatables are not copied -) - if type(table) ~= "table" then - return table - end - - local copy = {} - if not strip_metatables then - setmetatable(copy, getmetatable(table)) - end - for key, value in pairs(table) do - copy[deepcopy_tree(key)] = deepcopy_tree(value) - end - return copy -end -deepcopy_noncircular = deepcopy_tree - -function deepcopy( - table -- table to copy; reference equality will be preserved - , strip_metatables -- whether to strip metatables; falsy by default; metatables are not copied -) - local copies = {} - local function _deepcopy(table) - local copy = copies[table] - if copy then - return copy - end - copy = {} - if not strip_metatables then - setmetatable(copy, getmetatable(table)) - end - copies[table] = copy - local function _copy(value) - if type(value) ~= "table" then - return value - end - - if copies[value] then - return copies[value] - end - - return _deepcopy(value) - end - for key, value in pairs(table) do - copy[_copy(key)] = _copy(value) - end - return copy - end - return _deepcopy(table) -end - -copy = deepcopy - -function count(table) - local count = 0 - for _ in pairs(table) do - count = count + 1 - end - return count -end - -function count_equals(table, count) - local k - for _ = 1, count do - k = next(table, k) - if k == nil then return false end -- less than n keys - end - return next(table, k) == nil -- no (n + 1)th entry -end - -function is_empty(table) - return next(table) == nil -end - -function clear(table) - for k in pairs(table) do - table[k] = nil - end -end - -function foreach(table, func) - for k, v in pairs(table) do - func(k, v) - end -end - -function deep_foreach_any(table, func) - local seen = {} - local function visit(value) - func(value) - if type(value) == "table" then - if seen[value] then return end - seen[value] = true - for k, v in pairs(value) do - visit(k) - visit(v) - end - end - end - visit(table) -end - --- Recursively counts occurences of objects (non-primitives including strings) in a table. -function count_objects(value) - local counts = {} - if value == nil then - -- Early return for nil - return counts - end - local function count_values(value) - local type_ = type(value) - if type_ == "boolean" or type_ == "number" then return end - local count = counts[value] - counts[value] = (count or 0) + 1 - if not count and type_ == "table" then - for k, v in pairs(value) do - count_values(k) - count_values(v) - end - end - end - count_values(value) - return counts -end - -function foreach_value(table, func) - for _, v in pairs(table) do - func(v) - end -end - -function call(table, ...) - for _, func in pairs(table) do - func(...) - end -end - -function icall(table, ...) - for _, func in ipairs(table) do - func(...) - end -end - -function foreach_key(table, func) - for key, _ in pairs(table) do - func(key) - end -end - -function map(table, func) - for key, value in pairs(table) do - table[key] = func(value) - end - return table -end - -map_values = map - -function map_keys(table, func) - local new_tab = {} - for key, value in pairs(table) do - new_tab[func(key)] = value - end - return new_tab -end - -function process(tab, func) - local results = {} - for key, value in pairs(tab) do - table.insert(results, func(key, value)) - end - return results -end - -function call(funcs, ...) - for _, func in ipairs(funcs) do - func(...) - end -end - -function find(list, value) - for index, other_value in pairs(list) do - if value == other_value then - return index - end - end -end - -contains = find - -function to_add(table, after_additions) - local additions = {} - for key, value in pairs(after_additions) do - if table[key] ~= value then - additions[key] = value - end - end - return additions -end - -difference = to_add - -function deep_to_add(table, after_additions) - local additions = {} - for key, value in pairs(after_additions) do - if type(table[key]) == "table" and type(value) == "table" then - local sub_additions = deep_to_add(table[key], value) - if next(sub_additions) ~= nil then - additions[key] = sub_additions - end - elseif table[key] ~= value then - additions[key] = value - end - end - return additions -end - -function add_all(table, additions) - for key, value in pairs(additions) do - table[key] = value - end - return table -end - -function deep_add_all(table, additions) - for key, value in pairs(additions) do - if type(table[key]) == "table" and type(value) == "table" then - deep_add_all(table[key], value) - else - table[key] = value - end - end - return table -end - -function complete(table, completions) - for key, value in pairs(completions) do - if table[key] == nil then - table[key] = value - end - end - return table -end - -function deepcomplete(table, completions) - for key, value in pairs(completions) do - if table[key] == nil then - table[key] = value - elseif type(table[key]) == "table" and type(value) == "table" then - deepcomplete(table[key], value) - end - end - return table -end - -function merge(table, other_table, merge_func) - merge_func = merge_func or merge - local res = {} - for key, value in pairs(table) do - local other_value = other_table[key] - if other_value == nil then - res[key] = value - else - res[key] = merge_func(value, other_value) - end - end - for key, value in pairs(other_table) do - if table[key] == nil then - res[key] = value - end - end - return res -end - -function merge_tables(table, other_table) - return add_all(shallowcopy(table), other_table) -end - -union = merge_tables - -function intersection(table, other_table) - local result = {} - for key, value in pairs(table) do - if other_table[key] then - result[key] = value - end - end - return result -end - -function append(table, other_table) - local length = #table - for index, value in ipairs(other_table) do - table[length + index] = value - end - return table -end - -function keys(table) - local keys = {} - for key, _ in pairs(table) do - keys[#keys + 1] = key - end - return keys -end - -function values(table) - local values = {} - for _, value in pairs(table) do - values[#values + 1] = value - end - return values -end - -function flip(table) - local flipped = {} - for key, value in pairs(table) do - flipped[value] = key - end - return flipped -end - -function set(table) - local flipped = {} - for _, value in pairs(table) do - flipped[value] = true - end - return flipped -end - -function unique(table) - return keys(set(table)) -end - -function ivalues(table) - local index = 0 - return function() - index = index + 1 - return table[index] - end -end - -function rpairs(table) - local index = #table - return function() - if index >= 1 then - local value = table[index] - index = index - 1 - if value ~= nil then - return index + 1, value - end - end - end -end - --- Iterates the hash (= non-list) part of the table. The list part may not be modified while iterating. -function hpairs(table) - local len = #table -- length only has to be determined once as hnext is a closure - local function hnext(key) - local value - key, value = next(table, key) - if type(key) == "number" and key % 1 == 0 and key >= 1 and key <= len then -- list entry, skip - return hnext(key) - end - return key, value - end - return hnext -end - -function min_key(table, less_than) - less_than = less_than or lt - local min_key = next(table) - if min_key == nil then - return -- empty table - end - for candidate_key in next, table, min_key do - if less_than(candidate_key, min_key) then - min_key = candidate_key - end - end - return min_key -end - -function min_value(table, less_than) - less_than = less_than or lt - local min_key, min_value = next(table) - if min_key == nil then - return -- empty table - end - for candidate_key, candidate_value in next, table, min_key do - if less_than(candidate_value, min_value) then - min_key, min_value = candidate_key, candidate_value - end - end - return min_value, min_key -end - --- TODO move all of the below functions to modlib.list eventually - ---! deprecated -function default_comparator(value, other_value) - if value == other_value then - return 0 - end - if value > other_value then - return 1 - end - return -1 -end - ---! deprecated, use `binary_search(list, value, less_than)` instead ---> index if element found ---> -index for insertion if not found -function binary_search_comparator(comparator) - return function(list, value) - local min, max = 1, #list - while min <= max do - local pivot = min + math.floor((max - min) / 2) - local element = list[pivot] - local compared = comparator(value, element) - if compared == 0 then - return pivot - elseif compared > 0 then - min = pivot + 1 - else - max = pivot - 1 - end - end - return -min - end -end - -function binary_search( - list -- sorted list - , value -- value to be be searched for - , less_than -- function(a, b) return a < b end -) - less_than = less_than or lt - local min, max = 1, #list - while min <= max do - local mid = math.floor((min + max) / 2) - local element = list[mid] - if less_than(value, element) then - max = mid - 1 - elseif less_than(element, value) then - min = mid + 1 - else -- neither smaller nor larger => must be equal - return mid -- index if found - end - end - return nil, min -- nil, insertion index if not found -end - ---> whether the list is sorted in ascending order -function is_sorted(list, less_than --[[function(a, b) return a < b end]]) - less_than = less_than or function(a, b) return a < b end - for index = 2, #list do - if less_than(list[index], list[index - 1]) then - return false - end - end - return true -end - -function reverse(table) - local len = #table - for index = 1, len / 2 do - local index_from_end = len + 1 - index - table[index_from_end], table[index] = table[index], table[index_from_end] - end - return table -end - -function repetition(value, count) - local table = {} - for index = 1, count do - table[index] = value - end - return table -end - -function slice(list, from, to) - from, to = from or 1, to or #list - local res = {} - for i = from, to do - res[#res + 1] = list[i] - end - return res -end - --- JS-ish array splice -function splice( - list, -- to modify - start, -- index (inclusive) for where to start modifying the array (defaults to after the last element) - delete_count, -- how many elements to remove (defaults to `0`) - ... -- elements to insert after `start` -) - start, delete_count = start or (#list + 1), delete_count or 0 - if start < 0 then - start = start + #list + 1 - end - - local add_count = select("#", ...) - local shift = add_count - delete_count - if shift > 0 then -- shift up - for i = #list, start + delete_count, -1 do - list[i + shift] = list[i] - end - elseif shift < 0 then -- shift down - for i = start, #list do - list[i] = list[i - shift] - end - end - - -- Add elements - for i = 1, add_count do - list[start + i - 1] = select(i, ...) - end - - return list -end - --- Equivalent to to_list[to], ..., to_list[to + count] = from_list[from], ..., from_list[from + count] -function move(from_list, from, to, count, to_list) - from, to, count, to_list = from or 1, to or 1, count or #from_list, to_list or from_list - if to_list ~= from_list or to < from then - for i = 0, count do - to_list[to + i] = from_list[from + i] - end - else -- iterate in reverse order - for i = count, 0, -1 do - to_list[to + i] = from_list[from + i] - end - end -end - --- Export environment -return _ENV \ No newline at end of file diff --git a/mods/modlib/tex.lua b/mods/modlib/tex.lua deleted file mode 100644 index 006cdede..00000000 --- a/mods/modlib/tex.lua +++ /dev/null @@ -1,386 +0,0 @@ ---[[ - This file does not follow the usual conventions; - it duplicates some code for performance reasons. - - In particular, use of `modlib.minetest.colorspec` is avoided. - - Most methods operate *in-place* (imperative method names) - rather than returning a modified copy (past participle method names). - - Outside-facing methods consistently use 1-based indexing; indices are inclusive. -]] - -local min, max, floor, ceil = math.min, math.max, math.floor, math.ceil -local function round(x) return floor(x + 0.5) end -local function clamp(x, mn, mx) return max(min(x, mx), mn) end - --- ARGB handling utilities - -local function unpack_argb(argb) - return floor(argb / 0x1000000), - floor(argb / 0x10000) % 0x100, - floor(argb / 0x100) % 0x100, - argb % 0x100 -end - -local function pack_argb(a, r, g, b) - local argb = (((a * 0x100 + r) * 0x100) + g) * 0x100 + b - return argb -end - -local function round_argb(a, r, g, b) - return round(a), round(r), round(g), round(b) -end - -local function scale_0_1_argb(a, r, g, b) - return a / 255, r / 255, g / 255, b / 255 -end - -local function scale_0_255_argb(a, r, g, b) - return a * 255, r * 255, g * 255, b * 255 -end - -local tex = {} -local metatable = {__index = tex} - -function metatable:__eq(other) - if self.w ~= other.w or self.h ~= other.h then return false end - for i = 1, #self do if self[i] ~= other[i] then return false end end - return true -end - -function tex:new() - return setmetatable(self, metatable) -end - -function tex.filled(w, h, argb) - local self = {w = w, h = h} - for i = 1, w*h do - self[i] = argb - end - return tex.new(self) -end - -function tex:copy() - local copy = {w = self.w, h = self.h} - for i = 1, #self do - copy[i] = self[i] - end - return tex.new(copy) -end - --- Reading & writing - -function tex.read_png_string(str) - local stream = modlib.text.inputstream(str) - local png = modlib.minetest.decode_png(stream) - assert(stream:read(1) == nil, "eof expected") - modlib.minetest.convert_png_to_argb8(png) - png.data.w, png.data.h = png.width, png.height - return tex.new(png.data) -end - -function tex.read_png(path) - local png - modlib.file.with_open(path, "rb", function(f) - png = modlib.minetest.decode_png(f) - assert(f:read(1) == nil, "eof expected") - end) - modlib.minetest.convert_png_to_argb8(png) - png.data.w, png.data.h = png.width, png.height - return tex.new(png.data) -end - -function tex:write_png_string() - return modlib.minetest.encode_png(self.w, self.h, self) -end - -function tex:write_png(path) - modlib.file.write_binary(path, self:write_png_string()) -end - -function tex:fill(sx, sy, argb) - local w, h = self.w, self.h - for y = sy, h do - local i = (y - 1) * w + sx - for _ = sx, w do - self[i] = argb - i = i + 1 - end - end -end - -function tex:in_bounds(x, y) - return x >= 1 and y >= 1 and x <= self.w and y <= self.h -end - -function tex:get_argb_packed(x, y) - return self[(y - 1) * self.w + x] -end - -function tex:get_argb(x, y) - return unpack_argb(self[(y - 1) * self.w + x]) -end - -function tex:set_argb_packed(x, y, argb) - self[(y - 1) * self.w + x] = argb -end - -function tex:set_argb(x, y, a, r, g, b) - self[(y - 1) * self.w + x] = pack_argb(a, r, g, b) -end - -function tex:map_argb(func) - for i = 1, #self do - self[i] = pack_argb(func(unpack_argb(self[i]))) - end -end - -local function blit(s, x, y, t, o) - local sw, sh = s.w, s.h - local tw, th = t.w, t.h - -- Restrict to overlapping region - x, y = clamp(x, 1, sw), clamp(y, 1, sh) - local min_tx, min_ty = max(1, 2 - x), max(1, 2 - y) - local max_tx, max_ty = min(tw, sw - x + 1), min(th, sh - y + 1) - for ty = min_ty, max_ty do - local ti, si = (ty - 1) * tw, (y + ty - 2) * sw + x - 1 - for _ = min_tx, max_tx do - ti, si = ti + 1, si + 1 - local sa, sr, sg, sb = scale_0_1_argb(unpack_argb(s[si])) - if sa == 1 or not o then -- HACK because of dirty `[cracko` - local ta, tr, tg, tb = scale_0_1_argb(unpack_argb(t[ti])) - -- "`t` over `s`" (Porter-Duff-Algorithm) - local sata = sa * (1 - ta) - local ra = ta + sata - assert(ra > 0 or (sa == 0 and ta == 0)) - if ra > 0 then - s[si] = pack_argb(round_argb(scale_0_255_argb( - ra, - (ta * tr + sata * sr) / ra, - (ta * tg + sata * sg) / ra, - (ta * tb + sata * sb) / ra - ))) - end - end - end - end -end - --- Blitting with proper alpha blending. -function tex.blit(s, x, y, t) - return blit(s, x, y, t, false) -end - --- Blit, but only on fully opaque base pixels. Only `[cracko` uses this. -function tex.blito(s, x, y, t) - return blit(s, x, y, t, true) -end - -function tex.combine_argb(s, t, cf) - assert(#s == #t) - for i = 1, #s do - s[i] = cf(s[i], t[i]) - end -end - --- See https://github.com/TheAlgorithms/Lua/blob/162c4c59f5514c6115e0add8a2b4d56afd6d3204/src/bit/uint53/and.lua --- TODO (?) optimize fallback band using caching, move somewhere else -local band = bit and bit.band or function(n, m) - local res = 0 - local bit = 1 - while n * m ~= 0 do -- while both are nonzero - local n_bit, m_bit = n % 2, m % 2 -- extract LSB - res = res + (n_bit * m_bit) * bit -- add AND of LSBs - n, m = (n - n_bit) / 2, (m - m_bit) / 2 -- remove LSB from n & m - bit = bit * 2 -- next bit - end - return res -end - -function tex.band(s, t) - return s:combine_argb(t, band) -end - -function tex.hardlight_blend(s, t) - return s:combine_argb(t, function(sargb, targb) - local sa, sr, sg, sb = scale_0_1_argb(unpack_argb(sargb)) - local _, tr, tg, tb = scale_0_1_argb(unpack_argb(targb)) - return pack_argb(round_argb(scale_0_255_argb( - sa, - sr < 0.5 and 2*sr*tr or 1 - 2*(1-sr)*(1-tr), - sr < 0.5 and 2*sg*tg or 1 - 2*(1-sg)*(1-tg), - sr < 0.5 and 2*sb*tb or 1 - 2*(1-sb)*(1-tb) - ))) - end) -end - -function tex:brighten() - return self:map_argb(function(a, r, g, b) - return round_argb((255 + a) / 2, (255 + r) / 2, (255 + g) / 2, (255 + b) / 2) - end) -end - -function tex:noalpha() - for i = 1, #self do - self[i] = 0xFF000000 + self[i] % 0x1000000 - end -end - -function tex:makealpha(r, g, b) - local mrgb = r * 0x10000 + g * 0x100 + b - for i = 1, #self do - local rgb = self[i] % 0x1000000 - if rgb == mrgb then - self[i] = rgb - end - end -end - -function tex:opacity(factor) - for i = 1, #self do - self[i] = round(floor(self[i] / 0x1000000) * factor) * 0x1000000 + self[i] % 0x1000000 - end -end - -function tex:invert(ir, ig, ib, ia) - return self:map_argb(function(a, r, g, b) - if ia then a = 255 - a end - if ir then r = 255 - r end - if ig then g = 255 - g end - if ib then b = 255 - b end - return a, r, g, b - end) -end - -function tex:multiply_rgb(r, g, b) - return self:map_argb(function(sa, sr, sg, sb) - return round_argb(sa, r * sr, g * sg, b * sb) - end) -end - -function tex:screen_blend_rgb(r, g, b) - return self:map_argb(function(sa, sr, sg, sb) - return round_argb(sa, - 255 - ((255 - sr) * (255 - r)) / 255, - 255 - ((255 - sg) * (255 - g)) / 255, - 255 - ((255 - sb) * (255 - b)) / 255) - end) -end - -function tex:colorize(cr, cg, cb, ratio) - return self:map_argb(function(a, r, g, b) - local rat = ratio == "alpha" and a or ratio - return round_argb( - a, - rat * r + (1 - rat) * cr, - rat * g + (1 - rat) * cg, - rat * b + (1 - rat) * cb - ) - end) -end - -function tex:crop(from_x, from_y, to_x, to_y) - local w = self.w - local i = 1 - for y = from_y, to_y do - local j = (y - 1) * w + from_x - for _ = from_x, to_x do - self[i] = self[j] - i, j = i + 1, j + 1 - end - end - -- Remove remaining pixels - for j = i, #self do self[j] = nil end - self.w, self.h = to_x - from_x + 1, to_y - from_y + 1 -end - -function tex:flip_x() - for y = 1, self.h do - local i = (y - 1) * self.w - local j = i + self.w + 1 - while i < j do - i, j = i + 1, j - 1 - self[i], self[j] = self[j], self[i] - end - end -end - -function tex:flip_y() - for x = 1, self.w do - local i, j = x, (self.h - 1) * self.w + x - while i < j do - i, j = i + self.w, j - self.w - self[i], self[j] = self[j], self[i] - end - end -end - ---> copy of the texture, rotated 90 degrees clockwise -function tex:rotated_90() - local w, h = self.w, self.h - local t = {w = h, h = w} - local i = 0 - for y = 1, w do - for x = 1, h do - i = i + 1 - t[i] = self[(h-x)*w + y] - end - end - t = tex.new(t) - return t -end - --- Uses box sampling. Hard to optimize. --- TODO (...) interpolate box samples; match what Minetest does ---> copy of `self` resized to `w` x `h` -function tex:resized(w, h) - --! This function works with 0-based indices. - local sw, sh = self.w, self.h - local fx, fy = sw / w, sh / h - local t = {w = w, h = h} - local i = 0 - for y = 0, h - 1 do - for x = 0, w - 1 do - -- Sample the area - local vy_from = y * fy - local vy_to = vy_from + fy - local vx_from = x * fx - local vx_to = vx_from + fx - - local a, r, g, b = 0, 0, 0, 0 - local pf_sum = 0 - - local function blend(sx, sy, pf) - if pf <= 0 then return end - local sa, sr, sg, sb = unpack_argb(self[sy * sw + sx + 1]) - pf_sum = pf_sum + pf -- TODO (?) eliminate `pf_sum` - sa = sa * pf - a = a + sa - r, g, b = r + sa * sr, g + sa * sg, b + sa * sb - end - - local function srow(sy, pf) - if pf <= 0 then return end - local sx_from, sx_to = ceil(vx_from), floor(vx_to) - for sx = sx_from, sx_to - 1 do blend(sx, sy, pf) end -- whole pixels - -- Pixels at edges - blend(floor(vx_from), sy, pf * (sx_from - vx_from)) - blend(floor(vx_to), sy, pf * (vx_to - sx_to)) - end - - local sy_from, sy_to = ceil(vy_from), floor(vy_to) - for sy = sy_from, sy_to - 1 do srow(sy, 1) end -- whole pixels - -- Pixels at edges - srow(floor(vy_from), sy_from - vy_from) - srow(floor(vy_to), vy_to - sy_to) - if a > 0 then r, g, b = r / a, g / a, b / a end - assert(pf_sum > 0) - i = i + 1 - t[i] = pack_argb(round_argb(a / pf_sum, r, g, b)) - end - end - return tex.new(t) -end - -return tex diff --git a/mods/modlib/text.lua b/mods/modlib/text.lua deleted file mode 100644 index 3fe6226b..00000000 --- a/mods/modlib/text.lua +++ /dev/null @@ -1,189 +0,0 @@ --- Localize globals -local assert, math, modlib, setmetatable, string, table - = assert, math, modlib, setmetatable, string, table - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - -function upper_first(text) return text:sub(1, 1):upper() .. text:sub(2) end - -function lower_first(text) return text:sub(1, 1):lower() .. text:sub(2) end - -function starts_with(text, prefix) return text:sub(1, #prefix) == prefix end - -function ends_with(text, suffix) return text:sub(-#suffix) == suffix end - -function contains(str, substr, plain) - return not not str:find(substr, 1, plain == nil and true or plain) -end - -function trim_spacing(text) - return text:match"^%s*(.-)%s*$" -end - -local inputstream_metatable = { - __index = { - read = function(self, count) - local cursor = self.cursor + 1 - self.cursor = self.cursor + count - local text = self.text:sub(cursor, self.cursor) - return text ~= "" and text or nil - end, - seek = function(self) return self.cursor end - } -} ---> inputstream "handle"; only allows reading characters (given a count), seeking does not accept any arguments -function inputstream(text) - return setmetatable({text = text, cursor = 0}, inputstream_metatable) -end - -function hexdump(text) - local dump = {} - for index = 1, text:len() do - dump[index] = ("%02X"):format(text:byte(index)) - end - return table.concat(dump) -end - -function spliterator(str, delim, plain) - assert(delim ~= "") - local last_delim_end = 0 - - -- Iterator of possibly empty substrings between two matches of the delimiter - -- To exclude empty strings, filter the iterator or use `:gmatch"[...]+"` instead - return function() - if not last_delim_end then - return - end - - local delim_start, delim_end = str:find(delim, last_delim_end + 1, plain) - local substr - if delim_start then - substr = str:sub(last_delim_end + 1, delim_start - 1) - else - substr = str:sub(last_delim_end + 1) - end - last_delim_end = delim_end - return substr - end -end - -function split(text, delimiter, limit, plain) - limit = limit or math.huge - local parts = {} - local occurences = 1 - local last_index = 1 - local index = string.find(text, delimiter, 1, plain) - while index and occurences < limit do - table.insert(parts, string.sub(text, last_index, index - 1)) - last_index = index + string.len(delimiter) - index = string.find(text, delimiter, index + string.len(delimiter), plain) - occurences = occurences + 1 - end - table.insert(parts, string.sub(text, last_index)) - return parts -end - -function split_without_limit(text, delimiter, plain) - return split(text, delimiter, nil, plain) -end - -split_unlimited = split_without_limit - ---! Does not support Macintosh pre-OSX CR-only line endings ---! Deprecated in favor of the `lines` iterator below -function split_lines(text, limit) - return modlib.text.split(text, "\r?\n", limit, true) -end - --- When reading from a file, directly use `io.lines` instead --- Lines are possibly empty substrings separated by CR, LF or CRLF --- A trailing linefeed is ignored -function lines(str) - local line_start = 1 - -- Line iterator - return function() - if line_start > #str then - return - end - local linefeed_start, _, linefeed = str:find("([\r\n][\r\n]?)", line_start) - local line - if linefeed_start then - line = str:sub(line_start, linefeed_start - 1) - line_start = linefeed_start + (linefeed == "\r\n" and 2 or 1) - else - line = str:sub(line_start) - line_start = #str + 1 - end - return line - end -end - - -local zero = string.byte"0" -local nine = string.byte"9" -local letter_a = string.byte"A" -local letter_f = string.byte"F" - -function is_hexadecimal(byte) - return byte >= zero and byte <= nine or byte >= letter_a and byte <= letter_f -end - -magic_charset = "[" .. ("%^$+-*?.[]()"):gsub(".", "%%%1") .. "]" - -function escape_pattern(text) - return text:gsub(magic_charset, "%%%1") -end - -escape_magic_chars = escape_pattern - -local keywords = modlib.table.set{"and", "break", "do", "else", "elseif", "end", "false", "for", "function", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while"} -keywords["goto"] = true -- Lua 5.2 (LuaJIT) support - -function is_keyword(text) - return keywords[text] -end - -function is_identifier(text) - return (not keywords[text]) and text:match"^[A-Za-z_][A-Za-z%d_]*$" -end - -local function inextchar(text, i) - if i >= #text then return end - i = i + 1 - return i, text:sub(i, i) -end - -function ichars(text, start) - -- Iterator over `index, character` - return inextchar, text, (start or 1) - 1 -end - -local function inextbyte(text, i) - if i >= #text then return end - i = i + 1 - return i, text:byte(i, i) -end - -function ibytes(text, start) - -- Iterator over `index, byte` - return inextbyte, text, (start or 1) - 1 -end - -local function _random_bytes(count) - if count == 0 then return end - return math.random(0, 0xFF), _random_bytes(count - 1) -end - -function random_bytes( - -- number, how many random bytes the string should have, defaults to 1 - -- limited by stack size - count -) - count = count or 1 - return string.char(_random_bytes(count)) -end - --- Export environment -return _ENV diff --git a/mods/modlib/trie.lua b/mods/modlib/trie.lua deleted file mode 100644 index 3399b697..00000000 --- a/mods/modlib/trie.lua +++ /dev/null @@ -1,133 +0,0 @@ --- Localize globals -local math, next, pairs, setmetatable, string, table, unpack = math, next, pairs, setmetatable, string, table, unpack - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - -local metatable = {__index = _ENV} - --- Setting the metatable is fine as it does not contain single-character keys. --- TODO (?) encapsulate in "root" field for better code quality? -function new(table) return setmetatable(table or {}, metatable) end - -function insert(self, word, value, overwrite) - for i = 1, word:len() do - local char = word:sub(i, i) - self[char] = self[char] or {} - self = self[char] - end - local previous_value = self.value - if not previous_value or overwrite then self.value = value or true end - return previous_value -end - -function remove(self, word) - local branch, character = self, word:sub(1, 1) - for i = 1, word:len() - 1 do - local char = word:sub(i, i) - if not self[char] then return end - if self[char].value or next(self, next(self)) then - branch = self - character = char - end - self = self[char] - end - local char = word:sub(word:len()) - if not self[char] then return end - self = self[char] - local previous_value = self.value - self.value = nil - if branch and not next(self) then branch[character] = nil end - return previous_value -end - ---> value if found ---> nil else -function get(self, word) - for i = 1, word:len() do - local char = word:sub(i, i) - self = self[char] - if not self then return end - end - return self.value -end - -function suggestion(self, remainder) - local until_now = {} - local subtries = { [self] = until_now } - local suggestion, value - while next(subtries) do - local new_subtries = {} - local leaves = {} - for trie, word in pairs(subtries) do - if trie.value then table.insert(leaves, { word = word, value = trie.value }) end - end - if #leaves > 0 then - if remainder then - local best_leaves = {} - local best_score = 0 - for _, leaf in pairs(leaves) do - local score = 0 - for i = 1, math.min(#leaf.word, string.len(remainder)) do - -- calculate intersection - if remainder:sub(i, i) == leaf.word[i] then score = score + 1 end - end - if score == best_score then table.insert(best_leaves, leaf) - elseif score > best_score then best_leaves = { leaf } end - end - leaves = best_leaves - end - -- TODO select best instead of random - local leaf = leaves[math.random(1, #leaves)] - suggestion, value = table.concat(leaf.word), leaf.value - break - end - for trie, word in pairs(subtries) do - for char, subtrie in pairs(trie) do - local word = { unpack(word) } - table.insert(word, char) - new_subtries[subtrie] = word - end - end - subtries = new_subtries - end - return suggestion, value -end - ---> value if found ---> nil, suggestion, value of suggestion else -function search(self, word) - for i = 1, word:len() do - local char = word:sub(i, i) - if not self[char] then - local until_now = word:sub(1, i - 1) - local suggestion, value = suggestion(self, word:sub(i)) - return nil, until_now .. suggestion, value - end - self = self[char] - end - local value = self.value - if value then return value end - local until_now = word - local suggestion, value = suggestion(self) - return nil, until_now .. suggestion, value -end - -function find_longest(self, query, query_offset) - local leaf_pos = query_offset - local last_leaf - for i = query_offset, query:len() do - local char = query:sub(i, i) - self = self[char] - if not self then break - elseif self.value then - last_leaf = self.value - leaf_pos = i - end - end - return last_leaf, leaf_pos -end - --- Export environment -return _ENV \ No newline at end of file diff --git a/mods/modlib/utf8.lua b/mods/modlib/utf8.lua deleted file mode 100644 index 5ff0b10b..00000000 --- a/mods/modlib/utf8.lua +++ /dev/null @@ -1,102 +0,0 @@ -local assert, error, select, string_char, table_concat - = assert, error, select, string.char, table.concat - -local utf8 = {} - --- Overly permissive pattern that greedily matches a single UTF-8 codepoint -utf8.charpattern = "[%z-\127\194-\253][\128-\191]*" - -function utf8.is_valid_codepoint(codepoint) - -- Must be in bounds & must not be a surrogate - return codepoint <= 0x10FFFF and (codepoint < 0xD800 or codepoint > 0xDFFF) -end - -local function utf8_bytes(codepoint) - if codepoint <= 0x007F then - return codepoint - end if codepoint <= 0x7FF then - local payload_2 = codepoint % 0x40 - codepoint = (codepoint - payload_2) / 0x40 - return 0xC0 + codepoint, 0x80 + payload_2 - end if codepoint <= 0xFFFF then - local payload_3 = codepoint % 0x40 - codepoint = (codepoint - payload_3) / 0x40 - local payload_2 = codepoint % 0x40 - codepoint = (codepoint - payload_2) / 0x40 - return 0xE0 + codepoint, 0x80 + payload_2, 0x80 + payload_3 - end if codepoint <= 0x10FFFF then - local payload_4 = codepoint % 0x40 - codepoint = (codepoint - payload_4) / 0x40 - local payload_3 = codepoint % 0x40 - codepoint = (codepoint - payload_3) / 0x40 - local payload_2 = codepoint % 0x40 - codepoint = (codepoint - payload_2) / 0x40 - return 0xF0 + codepoint, 0x80 + payload_2, 0x80 + payload_3, 0x80 + payload_4 - end error"codepoint out of range" -end - -function utf8.char(...) - local n_args = select("#", ...) - if n_args == 0 then - return - end if n_args == 1 then - return string_char(utf8_bytes(...)) - end - local chars = {} - for i = 1, n_args do - chars[i] = string_char(utf8_bytes(select(i, ...))) - end - return table_concat(chars) -end - -local function utf8_next_codepoint(str, i) - local first_byte = str:byte(i) - if first_byte < 0x80 then - return i + 1, first_byte - end - - local len, head_bits - if first_byte >= 0xC0 and first_byte <= 0xDF then -- 110_00000 to 110_11111 - len, head_bits = 2, first_byte % 0x20 -- last 5 bits - elseif first_byte >= 0xE0 and first_byte <= 0xEF then -- 1110_0000 to 1110_1111 - len, head_bits = 3, first_byte % 0x10 -- last 4 bits - elseif first_byte >= 0xF0 and first_byte <= 0xF7 then -- 11110_000 to 11110_111 - len, head_bits = 4, first_byte % 0x8 -- last 3 bits - else error"invalid UTF-8" end - - local codepoint = 0 - local pow = 1 - for j = i + len - 1, i + 1, -1 do - local byte = assert(str:byte(j), "invalid UTF-8") - local val_bits = byte % 0x40 -- extract last 6 bits xxxxxx from 10xxxxxx - assert(byte - val_bits == 0x80) -- assert that first two bits are 10 - codepoint = codepoint + val_bits * pow - pow = pow * 0x40 - end - return i + len, codepoint + head_bits * pow -end - -function utf8.codepoint(str, i, j) - i, j = i or 1, j or #str - if i > j then return end - local codepoint - i, codepoint = utf8_next_codepoint(str, i) - assert(i - j <= 1, "invalid UTF-8") - return codepoint, utf8.codepoint(str, i) -end - --- Iterator to loop over the UTF-8 characters as `index, codepoint` -function utf8.codes(text, i) - i = i or 1 - return function() - if i > #text then - return - end - local prev_index = i - local codepoint - i, codepoint = utf8_next_codepoint(text, i) - return prev_index, codepoint - end -end - -return utf8 \ No newline at end of file diff --git a/mods/modlib/vararg.lua b/mods/modlib/vararg.lua deleted file mode 100644 index 86c5a689..00000000 --- a/mods/modlib/vararg.lua +++ /dev/null @@ -1,64 +0,0 @@ -local select, setmetatable, unpack = select, setmetatable, unpack - -local vararg = {} - -function vararg.aggregate(binary_func, initial, ...) - local total = initial - for i = 1, select("#", ...) do - total = binary_func(total, select(i, ...)) - end - return total -end - -local metatable = {__index = {}} - -function vararg.pack(...) - return setmetatable({["#"] = select("#", ...); ...}, metatable) -end - -local va = metatable.__index - -function va:unpack() - return unpack(self, 1, self["#"]) -end - -function va:select(n) - if n > self["#"] then return end - return self[n] -end - -local function inext(self, i) - i = i + 1 - if i > self["#"] then return end - return i, self[i] -end - -function va:ipairs() - return inext, self, 0 -end - -function va:concat(other) - local self_len, other_len = self["#"], other["#"] - local res = {["#"] = self_len + other_len} - for i = 1, self_len do - res[i] = self[i] - end - for i = 1, other_len do - res[self_len + i] = other[i] - end - return setmetatable(res, metatable) -end -metatable.__concat = va.concat - -function va:equals(other) - if self["#"] ~= other["#"] then return false end - for i = 1, self["#"] do if self[i] ~= other[i] then return false end end - return true -end -metatable.__eq = va.equals - -function va:aggregate(binary_func, initial) - return vararg.aggregate(binary_func, initial, self:unpack()) -end - -return vararg \ No newline at end of file diff --git a/mods/modlib/vector.lua b/mods/modlib/vector.lua deleted file mode 100644 index e37368e2..00000000 --- a/mods/modlib/vector.lua +++ /dev/null @@ -1,274 +0,0 @@ --- Localize globals -local assert, math, pairs, rawget, rawset, setmetatable, unpack, vector = assert, math, pairs, rawget, rawset, setmetatable, unpack, vector - --- Set environment -local _ENV = {} -setfenv(1, _ENV) - -local mt_vector = vector - -index_aliases = { - x = 1, - y = 2, - z = 3, - w = 4; - "x", "y", "z", "w"; -} - -metatable = { - __index = function(table, key) - local index = index_aliases[key] - if index ~= nil then - return rawget(table, index) - end - return _ENV[key] - end, - __newindex = function(table, key, value) - -- TODO - local index = index_aliases[key] - if index ~= nil then - return rawset(table, index, value) - end - return rawset(table, key, value) - end -} - -function new(v) - return setmetatable(v, metatable) -end - -function zeros(n) - local v = {} - for i = 1, n do - v[i] = 0 - end - return new(v) -end -function from_xyzw(v) - return new{v.x, v.y, v.z, v.w} -end - -function from_minetest(v) - return new{v.x, v.y, v.z} -end - -function to_xyzw(v) - return {x = v[1], y = v[2], z = v[3], w = v[4]} -end - ---+ not necessarily required, as Minetest respects the metatable -function to_minetest(v) - return mt_vector.new(unpack(v)) -end - -function equals(v, w) - for k, v in pairs(v) do - if v ~= w[k] then return false end - end - return true -end - -metatable.__eq = equals - -function combine(v, w, f) - local new_vector = {} - for key, value in pairs(v) do - new_vector[key] = f(value, w[key]) - end - return new(new_vector) -end - -function apply(v, f, ...) - local new_vector = {} - for key, value in pairs(v) do - new_vector[key] = f(value, ...) - end - return new(new_vector) -end - -function combinator(f) - return function(v, w) - return combine(v, w, f) - end, function(v, ...) - return apply(v, f, ...) - end -end - -function invert(v) - local res = {} - for key, value in pairs(v) do - res[key] = -value - end - return new(res) -end - -add, add_scalar = combinator(function(v, w) return v + w end) -subtract, subtract_scalar = combinator(function(v, w) return v - w end) -multiply, multiply_scalar = combinator(function(v, w) return v * w end) -divide, divide_scalar = combinator(function(v, w) return v / w end) -pow, pow_scalar = combinator(function(v, w) return v ^ w end) - -metatable.__add = add -metatable.__unm = invert -metatable.__sub = subtract -metatable.__mul = multiply -metatable.__div = divide - ---+ linear interpolation ---: ratio number from 0 (all the first vector) to 1 (all the second vector) -function interpolate(v, w, ratio) - return add(multiply_scalar(v, 1 - ratio), multiply_scalar(w, ratio)) -end - -function norm(v) - local sum = 0 - for _, value in pairs(v) do - sum = sum + value ^ 2 - end - return sum -end - -function length(v) - return math.sqrt(norm(v)) -end - --- Minor code duplication for the sake of performance -function distance(v, w) - local sum = 0 - for key, value in pairs(v) do - sum = sum + (value - w[key]) ^ 2 - end - return math.sqrt(sum) -end - -function normalize(v) - return divide_scalar(v, length(v)) -end - -function normalize_zero(v) - local len = length(v) - if len == 0 then - -- Return a zeroed vector with the same keys - local zeroed = {} - for k in pairs(v) do - zeroed[k] = 0 - end - return new(zeroed) - end - return divide_scalar(v, len) -end - -function floor(v) - return apply(v, math.floor) -end - -function ceil(v) - return apply(v, math.ceil) -end - -function clamp(v, min, max) - return apply(apply(v, math.max, min), math.min, max) -end - -function cross3(v, w) - assert(#v == 3 and #w == 3) - return new{ - v[2] * w[3] - v[3] * w[2], - v[3] * w[1] - v[1] * w[3], - v[1] * w[2] - v[2] * w[1] - } -end - -function dot(v, w) - local sum = 0 - for i, c in pairs(v) do - sum = sum + c * w[i] - end - return sum -end - -function reflect(v, normal --[[**normalized** plane normal vector]]) - return subtract(v, multiply_scalar(normal, 2 * dot(v, normal))) -- reflection of v at the plane -end - ---+ Angle between two vectors ---> Signed angle in radians -function angle(v, w) - -- Based on dot(v, w) = |v| * |w| * cos(x) - return math.acos(dot(v, w) / length(v) / length(w)) -end - --- See https://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToAngle/ -function axis_angle3(euler_rotation) - assert(#euler_rotation == 3) - euler_rotation = divide_scalar(euler_rotation, 2) - local cos = apply(euler_rotation, math.cos) - local sin = apply(euler_rotation, math.sin) - return normalize_zero{ - sin[1] * sin[2] * cos[3] + cos[1] * cos[2] * sin[3], - sin[1] * cos[2] * cos[3] + cos[1] * sin[2] * sin[3], - cos[1] * sin[2] * cos[3] - sin[1] * cos[2] * sin[3], - }, 2 * math.acos(cos[1] * cos[2] * cos[3] - sin[1] * sin[2] * sin[3]) -end - --- Uses Rodrigues' rotation formula --- axis must be normalized -function rotate3(v, axis, angle) - assert(#v == 3 and #axis == 3) - local cos = math.cos(angle) - return multiply_scalar(v, cos) - -- Minetest's coordinate system is *left-handed*, so `v` and `axis` must be swapped here - + multiply_scalar(cross3(v, axis), math.sin(angle)) - + multiply_scalar(axis, dot(axis, v) * (1 - cos)) -end - -function box_box_collision(diff, box, other_box) - for index, diff in pairs(diff) do - if box[index] + diff > other_box[index + 3] or other_box[index] > box[index + 3] + diff then - return false - end - end - return true -end - -local function moeller_trumbore(origin, direction, triangle, is_tri) - local point_1, point_2, point_3 = unpack(triangle) - local edge_1, edge_2 = subtract(point_2, point_1), subtract(point_3, point_1) - local h = cross3(direction, edge_2) - local a = dot(edge_1, h) - if math.abs(a) < 1e-9 then - return - end - local f = 1 / a - local diff = subtract(origin, point_1) - local u = f * dot(diff, h) - if u < 0 or u > 1 then - return - end - local q = cross3(diff, edge_1) - local v = f * dot(direction, q) - if v < 0 or (is_tri and u or 0) + v > 1 then - return - end - local pos_on_line = f * dot(edge_2, q) - if pos_on_line >= 0 then - return pos_on_line, u, v - end -end - -function ray_triangle_intersection(origin, direction, triangle) - return moeller_trumbore(origin, direction, triangle, true) -end - -function ray_parallelogram_intersection(origin, direction, parallelogram) - return moeller_trumbore(origin, direction, parallelogram) -end - -function triangle_normal(triangle) - local point_1, point_2, point_3 = unpack(triangle) - local edge_1, edge_2 = subtract(point_2, point_1), subtract(point_3, point_1) - return normalize(cross3(edge_1, edge_2)) -end - --- Export environment -return _ENV \ No newline at end of file diff --git a/mods/modlib/web.lua b/mods/modlib/web.lua deleted file mode 100644 index 4a1ba972..00000000 --- a/mods/modlib/web.lua +++ /dev/null @@ -1,7 +0,0 @@ -return setmetatable({}, {__index = function(self, module_name) - if module_name == "uri" or module_name == "html" then - local module = assert(loadfile(modlib.mod.get_resource(modlib.modname, "web", module_name .. ".lua")))() - self[module_name] = module - return module - end -end}) \ No newline at end of file diff --git a/mods/modlib/web/html.lua b/mods/modlib/web/html.lua deleted file mode 100644 index 9b141d43..00000000 --- a/mods/modlib/web/html.lua +++ /dev/null @@ -1,27 +0,0 @@ -local html = setmetatable({}, {__index = function(self, key) - if key == "unescape" then - local func = assert(loadfile(modlib.mod.get_resource("modlib", "web", "html", "entities.lua"))) - setfenv(func, {}) - local named_entities = assert(func()) - local function unescape(text) - return text - :gsub("&([A-Za-z]+);", named_entities) -- named - :gsub("&#(%d+);", function(digits) return modlib.utf8.char(tonumber(digits)) end) -- decimal - :gsub("&#x(%x+);", function(digits) return modlib.utf8.char(tonumber(digits, 16)) end) -- hex - end - self.unescape = unescape - return unescape - end -end}) - -function html.escape(text) - return text:gsub(".", { - ["<"] = "<", - [">"] = ">", - ["&"] = "&", - ["'"] = "'", - ['"'] = """, - }) -end - -return html \ No newline at end of file diff --git a/mods/modlib/web/html/entities.lua b/mods/modlib/web/html/entities.lua deleted file mode 100644 index f9c77c2b..00000000 --- a/mods/modlib/web/html/entities.lua +++ /dev/null @@ -1,3 +0,0 @@ --- HTML entity lookup table generated by build/html_entities.lua. Do not edit. -return {["llhard"] = "ā„«", ["ugrav"] = "ù", ["gamma"] = "γ", ["copf"] = "š•”", ["napprox"] = "≉", ["fltns"] = "ā–±", ["amacr"] = "ā", ["NotGreaterFullEqual"] = "≧̸", ["SquareSuperset"] = "⊐", ["ffllig"] = "ffl", ["Iacut"] = "ƍ", ["nrarr"] = "↛", ["sup2"] = "²", ["llarr"] = "⇇", ["measuredangle"] = "∔", ["divonx"] = "⋇", ["boxvL"] = "ā•”", ["range"] = "⦄", ["Jscr"] = "š’„", ["ncap"] = "⩃", ["ndash"] = "–", ["varpropto"] = "āˆ", ["yacy"] = "я", ["gEl"] = "⪌", ["div"] = "Ć·", ["mfr"] = "š”Ŗ", ["sext"] = "✶", ["darr"] = "↓", ["CapitalDifferentialD"] = "ā……", ["ufr"] = "š”²", ["Scirc"] = "Ŝ", ["twixt"] = "≬", ["num"] = "#", ["Zeta"] = "Ī–", ["ruluhar"] = "ℨ", ["CHcy"] = "Ч", ["wreath"] = "≀", ["Mfr"] = "š”", ["de"] = "°", ["lpar"] = "(", ["isinv"] = "∈", ["HumpEqual"] = "ā‰", ["frasl"] = "⁄", ["Ufr"] = "š”˜", ["le"] = "≤", ["curlywedge"] = "ā‹", ["NotGreaterSlantEqual"] = "⩾̸", ["Egrave"] = "ƈ", ["subsetneqq"] = "ā«‹", ["nLeftarrow"] = "ā‡", ["angst"] = "ƅ", ["supseteq"] = "āŠ‡", ["ltdot"] = "ā‹–", ["int"] = "∫", ["permil"] = "‰", ["equest"] = "ā‰Ÿ", ["eqcirc"] = "≖", ["ubrcy"] = "ў", ["micro"] = "µ", ["Ecirc"] = "Ê", ["Fouriertrf"] = "ℱ", ["dotplus"] = "āˆ”", ["ycirc"] = "Å·", ["boxdL"] = "ā••", ["LeftDoubleBracket"] = "⟦", ["Yopf"] = "š•", ["Int"] = "∬", ["kscr"] = "š“€", ["GreaterGreater"] = "āŖ¢", ["lsquor"] = "ā€š", ["mDDot"] = "∺", ["late"] = "āŖ­", ["boxh"] = "─", ["sqcup"] = "āŠ”", ["quot"] = "\"", ["LessTilde"] = "≲", ["ldsh"] = "↲", ["lrhard"] = "ā„­", ["ropf"] = "š•£", ["larrtl"] = "↢", ["comma"] = ",", ["DoubleRightArrow"] = "⇒", ["acy"] = "а", ["gnapprox"] = "⪊", ["boxUl"] = "ā•œ", ["plustwo"] = "⨧", ["Rarr"] = "↠", ["icy"] = "Šø", ["grave"] = "`", ["nwArr"] = "⇖", ["pitchfork"] = "ā‹”", ["tosa"] = "⤩", ["Leftarrow"] = "⇐", ["Yscr"] = "š’“", ["hoarr"] = "⇿", ["ycy"] = "ы", ["subne"] = "⊊", ["lescc"] = "āŖØ", ["bigcap"] = "ā‹‚", ["marker"] = "ā–®", ["Acy"] = "А", ["auml"] = "Ƥ", ["NotLess"] = "≮", ["nrtrie"] = "ā‹­", ["zeta"] = "ζ", ["egrave"] = "ĆØ", ["Icy"] = "И", ["Oslas"] = "Ƙ", ["ddagger"] = "—", ["succ"] = "≻", ["leftarrow"] = "←", ["iprod"] = "⨼", ["eqcolon"] = "≕", ["RightDownTeeVector"] = "ā„", ["Del"] = "āˆ‡", ["omacr"] = "ō", ["zigrarr"] = "ā‡", ["Ycy"] = "Š«", ["LeftArrowRightArrow"] = "⇆", ["becaus"] = "∵", ["MinusPlus"] = "āˆ“", ["nvinfin"] = "ā§ž", ["nvle"] = "ā‰¤āƒ’", ["NonBreakingSpace"] = "Ā ", ["CounterClockwiseContourIntegral"] = "∳", ["sharp"] = "♯", ["ugrave"] = "ù", ["boxVR"] = "ā• ", ["NotLeftTriangleBar"] = "ā§Ģø", ["rppolint"] = "⨒", ["egsdot"] = "⪘", ["NestedLessLess"] = "≪", ["DoubleVerticalBar"] = "∄", ["Ycirc"] = "Ŷ", ["ogt"] = "⧁", ["smte"] = "āŖ¬", ["Hscr"] = "ā„‹", ["sup3"] = "³", ["cudarrl"] = "⤸", ["Copf"] = "ā„‚", ["lbarr"] = "⤌", ["igrav"] = "Ƭ", ["xdtri"] = "ā–½", ["nequiv"] = "≢", ["rangd"] = "⦒", ["vartheta"] = "Ļ‘", ["ecaron"] = "ě", ["IEcy"] = "Š•", ["nearrow"] = "↗", ["Auml"] = "Ƅ", ["iacut"] = "Ć­", ["subseteq"] = "āŠ†", ["Dashv"] = "⫤", ["mopf"] = "š•ž", ["NotReverseElement"] = "∌", ["DD"] = "ā……", ["udblac"] = "ű", ["cirmid"] = "⫯", ["bigwedge"] = "ā‹€", ["timesbar"] = "⨱", ["vzigzag"] = "⦚", ["angmsdac"] = "⦪", ["ratio"] = "∶", ["ZHcy"] = "Š–", ["DoubleRightTee"] = "⊨", ["zscr"] = "š“", ["subplus"] = "āŖæ", ["notinvc"] = "ā‹¶", ["acd"] = "∿", ["ggg"] = "ā‹™", ["rarr"] = "→", ["checkmark"] = "āœ“", ["NegativeMediumSpace"] = "​", ["fllig"] = "fl", ["npreceq"] = "āŖÆĢø", ["LeftVectorBar"] = "ā„’", ["check"] = "āœ“", ["veeeq"] = "ā‰š", ["ges"] = "⩾", ["Delta"] = "Ī”", ["Ugrav"] = "ƙ", ["AElig"] = "Ɔ", ["angzarr"] = "ā¼", ["horbar"] = "―", ["fallingdotseq"] = "≒", ["exist"] = "∃", ["curlyeqprec"] = "ā‹ž", ["hbar"] = "ā„", ["SubsetEqual"] = "āŠ†", ["Kscr"] = "š’¦", ["intlarhk"] = "⨗", ["GreaterSlantEqual"] = "⩾", ["real"] = "ā„œ", ["VerticalBar"] = "∣", ["leftarrowtail"] = "↢", ["ogon"] = "Ė›", ["squf"] = "ā–Ŗ", ["hercon"] = "⊹", ["supsim"] = "⫈", ["urcrop"] = "āŒŽ", ["REG"] = "Ā®", ["Ropf"] = "ā„", ["dd"] = "ā…†", ["boxuR"] = "ā•˜", ["lneq"] = "āŖ‡", ["leftrightarrows"] = "⇆", ["Dopf"] = "š”»", ["bigstar"] = "ā˜…", ["precsim"] = "≾", ["eacute"] = "Ć©", ["block"] = "ā–ˆ", ["NotSucceedsEqual"] = "āŖ°Ģø", ["rarrap"] = "ℵ", ["nmid"] = "∤", ["chi"] = "χ", ["RightUpVector"] = "↾", ["simplus"] = "⨤", ["shcy"] = "ш", ["nbs"] = "Ā ", ["weierp"] = "ā„˜", ["sh"] = "Ā­", ["yscr"] = "š“Ž", ["erDot"] = "≓", ["hscr"] = "š’½", ["curren"] = "¤", ["RightTeeArrow"] = "↦", ["colon"] = ":", ["mp"] = "āˆ“", ["nsce"] = "āŖ°Ģø", ["mho"] = "ā„§", ["complexes"] = "ā„‚", ["Udblac"] = "Ű", ["intprod"] = "⨼", ["lnsim"] = "⋦", ["sbquo"] = "ā€š", ["prap"] = "āŖ·", ["ohm"] = "Ī©", ["DoubleUpArrow"] = "⇑", ["rmoust"] = "āŽ±", ["uum"] = "ü", ["rationals"] = "ā„š", ["dfr"] = "š””", ["NotLeftTriangle"] = "⋪", ["flat"] = "ā™­", ["lfr"] = "š”©", ["eum"] = "Ć«", ["LessEqualGreater"] = "ā‹š", ["tfr"] = "š”±", ["trianglerighteq"] = "⊵", ["varsupsetneqq"] = "ā«Œļø€", ["Aogon"] = "Ą", ["Uum"] = "Ü", ["dlcorn"] = "āŒž", ["solb"] = "ā§„", ["diamondsuit"] = "♦", ["reg"] = "Ā®", ["Lfr"] = "š”", ["Tab"] = "\9", ["frac56"] = "ā…š", ["Eum"] = "Ƌ", ["circleddash"] = "āŠ", ["fork"] = "ā‹”", ["nsup"] = "āŠ…", ["Tfr"] = "š”—", ["leftleftarrows"] = "⇇", ["angmsdab"] = "⦩", ["easter"] = "ā©®", ["InvisibleTimes"] = "⁢", ["straightphi"] = "Ļ•", ["looparrowright"] = "↬", ["triangle"] = "ā–µ", ["xi"] = "ξ", ["eqslantgtr"] = "āŖ–", ["pi"] = "Ļ€", ["Succeeds"] = "≻", ["suphsol"] = "āŸ‰", ["icir"] = "Ć®", ["gtrdot"] = "ā‹—", ["Icir"] = "Ǝ", ["Xi"] = "Īž", ["iukcy"] = "і", ["phone"] = "ā˜Ž", ["alefsym"] = "ℵ", ["succeq"] = "āŖ°", ["uparrow"] = "↑", ["drcorn"] = "⌟", ["bsolb"] = "ā§…", ["lhblk"] = "ā–„", ["nrightarrow"] = "↛", ["xrarr"] = "⟶", ["timesd"] = "⨰", ["lrm"] = "ā€Ž", ["ltcir"] = "⩹", ["blacktriangle"] = "ā–“", ["boxH"] = "═", ["VerticalTilde"] = "≀", ["olarr"] = "↺", ["nsc"] = "⊁", ["npolint"] = "⨔", ["ccups"] = "⩌", ["succcurlyeq"] = "≽", ["pcy"] = "Šæ", ["NotLeftTriangleEqual"] = "⋬", ["geqslant"] = "⩾", ["NotTilde"] = "≁", ["qint"] = "⨌", ["lbbrk"] = "ā²", ["shy"] = "Ā­", ["squ"] = "ā–”", ["timesb"] = "⊠", ["Uparrow"] = "⇑", ["cularrp"] = "⤽", ["SHCHcy"] = "Š©", ["dscy"] = "ѕ", ["PlusMinus"] = "±", ["lopar"] = "⦅", ["nLeftrightarrow"] = "ā‡Ž", ["Ncaron"] = "Ň", ["aelig"] = "Ʀ", ["sscr"] = "š“ˆ", ["Lambda"] = "Ī›", ["Rang"] = "⟫", ["xcirc"] = "ā—Æ", ["not"] = "¬", ["Lscr"] = "ā„’", ["rarrpl"] = "ā„…", ["vdash"] = "⊢", ["copy"] = "Ā©", ["hairsp"] = "ā€Š", ["GreaterFullEqual"] = "≧", ["dtdot"] = "⋱", ["LeftDownVectorBar"] = "ā„™", ["approxeq"] = "ā‰Š", ["Prime"] = "″", ["dblac"] = "Ė", ["notni"] = "∌", ["vsubne"] = "āŠŠļø€", ["suplarr"] = "ā„»", ["setmn"] = "āˆ–", ["gneqq"] = "≩", ["nles"] = "⩽̸", ["clubsuit"] = "♣", ["tridot"] = "ā—¬", ["nleftrightarrow"] = "↮", ["ncaron"] = "ň", ["lessgtr"] = "≶", ["gbreve"] = "ğ", ["notinva"] = "āˆ‰", ["lambda"] = "Ī»", ["ape"] = "ā‰Š", ["imacr"] = "Ä«", ["thkap"] = "ā‰ˆ", ["multimap"] = "⊸", ["fflig"] = "ff", ["NotLessTilde"] = "≓", ["boxhU"] = "╨", ["supplus"] = "ā«€", ["lowbar"] = "_", ["rarrsim"] = "ā„“", ["thetasym"] = "Ļ‘", ["image"] = "ā„‘", ["upsi"] = "Ļ…", ["nsmid"] = "∤", ["gtrsim"] = "≳", ["Lmidot"] = "Äæ", ["nap"] = "≉", ["Oacut"] = "Ɠ", ["ZeroWidthSpace"] = "​", ["aacut"] = "Ć”", ["boxvl"] = "┤", ["Colon"] = "∷", ["nsupe"] = "āŠ‰", ["bump"] = "ā‰Ž", ["uacut"] = "Ćŗ", ["Wcirc"] = "Å“", ["LeftRightVector"] = "ā„Ž", ["rHar"] = "ℤ", ["nless"] = "≮", ["DoubleLongRightArrow"] = "⟹", ["capcap"] = "ā©‹", ["lltri"] = "ā—ŗ", ["caron"] = "ˇ", ["upsih"] = "Ļ’", ["DownLeftRightVector"] = "ℐ", ["nVDash"] = "⊯", ["intercal"] = "⊺", ["lesdot"] = "⩿", ["mscr"] = "š“‚", ["acute"] = "Ā“", ["Uacute"] = "Ú", ["nlArr"] = "ā‡", ["rpargt"] = "⦔", ["zdot"] = "ż", ["incare"] = "ā„…", ["epar"] = "ā‹•", ["mapstoleft"] = "↤", ["nsubseteqq"] = "ā«…Ģø", ["ctdot"] = "⋯", ["leq"] = "≤", ["there4"] = "∓", ["OpenCurlyQuote"] = "ā€˜", ["andslope"] = "⩘", ["eDDot"] = "ā©·", ["frac13"] = "ā…“", ["Oscr"] = "š’Ŗ", ["Implies"] = "⇒", ["OElig"] = "Œ", ["drcrop"] = "⌌", ["gtrapprox"] = "āŖ†", ["RightUpTeeVector"] = "ℜ", ["oslas"] = "Ćø", ["empty"] = "āˆ…", ["xopf"] = "š•©", ["ocir"] = "⊚", ["NotPrecedesSlantEqual"] = "ā‹ ", ["Ocir"] = "Ɣ", ["blacktriangleleft"] = "ā—‚", ["ordf"] = "ĀŖ", ["lacute"] = "Äŗ", ["RE"] = "Ā®", ["trianglelefteq"] = "⊓", ["HumpDownHump"] = "ā‰Ž", ["frac58"] = "ā…", ["NotTildeTilde"] = "≉", ["Nopf"] = "ā„•", ["csupe"] = "ā«’", ["Lang"] = "⟪", ["Lleftarrow"] = "ā‡š", ["rceil"] = "āŒ‰", ["vsubnE"] = "ā«‹ļø€", ["Idot"] = "İ", ["DDotrahd"] = "⤑", ["gl"] = "≷", ["tilde"] = "˜", ["Assign"] = "≔", ["utri"] = "ā–µ", ["OverBrace"] = "āž", ["odsold"] = "⦼", ["robrk"] = "⟧", ["Bscr"] = "ℬ", ["DiacriticalDoubleAcute"] = "Ė", ["coloneq"] = "≔", ["blk34"] = "ā–“", ["Phi"] = "Φ", ["cupcup"] = "⩊", ["sqsubseteq"] = "āŠ‘", ["NotSubset"] = "āŠ‚āƒ’", ["nsubseteq"] = "⊈", ["larrlp"] = "↫", ["odot"] = "āŠ™", ["niv"] = "āˆ‹", ["therefore"] = "∓", ["nbump"] = "ā‰ŽĢø", ["kfr"] = "š”Ø", ["lbrkslu"] = "ā¦", ["sfr"] = "š”°", ["midast"] = "*", ["micr"] = "µ", ["filig"] = "fi", ["wcirc"] = "ŵ", ["esdot"] = "≐", ["Sqrt"] = "√", ["omega"] = "ω", ["lArr"] = "⇐", ["sdotb"] = "⊔", ["approx"] = "ā‰ˆ", ["nsubset"] = "āŠ‚āƒ’", ["Kfr"] = "š”Ž", ["Qopf"] = "ā„š", ["nvrtrie"] = "āŠµāƒ’", ["latail"] = "⤙", ["leqslant"] = "⩽", ["PrecedesEqual"] = "āŖÆ", ["lesg"] = "ā‹šļø€", ["re"] = "Ā®", ["ii"] = "ā…ˆ", ["plusdo"] = "āˆ”", ["NotHumpEqual"] = "ā‰Ģø", ["rbrksld"] = "ā¦Ž", ["angmsdad"] = "⦫", ["cscr"] = "š’ø", ["qprime"] = "⁗", ["rsquor"] = "’", ["rarrbfs"] = "⤠", ["bsemi"] = "ā", ["TSHcy"] = "Š‹", ["complement"] = "∁", ["ulcorn"] = "⌜", ["orarr"] = "↻", ["veebar"] = "⊻", ["boxhu"] = "┓", ["CloseCurlyQuote"] = "’", ["urcorner"] = "āŒ", ["lsim"] = "≲", ["COP"] = "Ā©", ["triangleleft"] = "ā—ƒ", ["nu"] = "ν", ["OverBar"] = "‾", ["Esim"] = "⩳", ["nlE"] = "≦̸", ["nrarrw"] = "ā†Ģø", ["HARDcy"] = "ŠŖ", ["sstarf"] = "⋆", ["jopf"] = "š•›", ["smile"] = "⌣", ["urcorn"] = "āŒ", ["G"] = ">", ["ngeq"] = "≱", ["rdquo"] = "ā€", ["udarr"] = "⇅", ["capcup"] = "⩇", ["mdash"] = "—", ["boxbox"] = "⧉", ["tshcy"] = "ћ", ["LessFullEqual"] = "≦", ["DoubleContourIntegral"] = "∯", ["Nu"] = "Ī", ["lang"] = "⟨", ["oline"] = "‾", ["wedge"] = "∧", ["CupCap"] = "ā‰", ["Kcy"] = "К", ["laquo"] = "Ā«", ["compfn"] = "∘", ["Scy"] = "Š”", ["simg"] = "āŖž", ["gtcir"] = "⩺", ["loplus"] = "⨭", ["nlarr"] = "ā†š", ["softcy"] = "ь", ["ENG"] = "Ŋ", ["thorn"] = "þ", ["NotSucceedsSlantEqual"] = "ā‹”", ["pr"] = "≺", ["harrcir"] = "℈", ["NoBreak"] = "⁠", ["pound"] = "Ā£", ["upsilon"] = "Ļ…", ["Poincareplane"] = "ā„Œ", ["Product"] = "āˆ", ["Iogon"] = "Ä®", ["Congruent"] = "≔", ["rtriltri"] = "ā§Ž", ["xhArr"] = "⟺", ["plusm"] = "±", ["mid"] = "∣", ["TScy"] = "Ц", ["boxuL"] = "ā•›", ["Pr"] = "āŖ»", ["triangleright"] = "ā–¹", ["Rsh"] = "↱", ["Bernoullis"] = "ℬ", ["Gcedil"] = "Ä¢", ["Upsi"] = "Ļ’", ["gneq"] = "⪈", ["radic"] = "√", ["SmallCircle"] = "∘", ["bepsi"] = "϶", ["frac3"] = "¾", ["eta"] = "Ī·", ["operp"] = "⦹", ["g"] = ">", ["cross"] = "āœ—", ["epsilon"] = "ε", ["sigmav"] = "Ļ‚", ["congdot"] = "ā©­", ["frac45"] = "ā…˜", ["target"] = "āŒ–", ["Vdash"] = "⊩", ["alpha"] = "α", ["shortmid"] = "∣", ["odblac"] = "ő", ["gtreqqless"] = "⪌", ["ntgl"] = "≹", ["gap"] = "āŖ†", ["Eta"] = "Ī—", ["Icirc"] = "Ǝ", ["bumpE"] = "āŖ®", ["Atild"] = "ƃ", ["gne"] = "⪈", ["vscr"] = "š“‹", ["boxDl"] = "ā•–", ["Yuml"] = "Åø", ["nesim"] = "≂̸", ["angmsdag"] = "⦮", ["apE"] = "ā©°", ["bigcup"] = "ā‹ƒ", ["atild"] = "Ć£", ["searhk"] = "⤄", ["eng"] = "ŋ", ["Cscr"] = "š’ž", ["lgE"] = "āŖ‘", ["cong"] = "≅", ["LeftDownVector"] = "ā‡ƒ", ["Jcirc"] = "Ä“", ["Lacute"] = "Ĺ", ["InvisibleComma"] = "⁣", ["lesdoto"] = "⪁", ["eqslantless"] = "āŖ•", ["lesssim"] = "≲", ["kappav"] = "ϰ", ["nshortmid"] = "∤", ["boxhD"] = "ā•„", ["lstrok"] = "ł", ["acut"] = "Ā“", ["CircleDot"] = "āŠ™", ["vArr"] = "⇕", ["uogon"] = "ų", ["nisd"] = "⋺", ["curarr"] = "↷", ["Dstrok"] = "Đ", ["gtcc"] = "āŖ§", ["topf"] = "š•„", ["rdsh"] = "↳", ["cwconint"] = "∲", ["prime"] = "′", ["ReverseElement"] = "āˆ‹", ["lharul"] = "K", ["trade"] = "ā„¢", ["Jopf"] = "š•", ["rfisht"] = "ℽ", ["dzigrarr"] = "⟿", ["Odblac"] = "Ő", ["plus"] = "+", ["capand"] = "ā©„", ["nis"] = "⋼", ["cacute"] = "ć", ["RightUpDownVector"] = "ā„", ["frac15"] = "ā…•", ["geqq"] = "≧", ["iexc"] = "Ā”", ["cdot"] = "ċ", ["uscr"] = "š“Š", ["vnsub"] = "āŠ‚āƒ’", ["cop"] = "Ā©", ["ll"] = "≪", ["bbrk"] = "āŽµ", ["kjcy"] = "ќ", ["dstrok"] = "đ", ["UnionPlus"] = "āŠŽ", ["gtdot"] = "ā‹—", ["ecolon"] = "≕", ["sqsupe"] = "āŠ’", ["swArr"] = "⇙", ["quo"] = "\"", ["RightTee"] = "⊢", ["lbrace"] = "{", ["Ll"] = "ā‹˜", ["gsim"] = "≳", ["subsim"] = "⫇", ["aogon"] = "ą", ["lozenge"] = "ā—Š", ["NotNestedGreaterGreater"] = "⪢̸", ["blacklozenge"] = "ā§«", ["mapstodown"] = "↧", ["tritime"] = "⨻", ["mumap"] = "⊸", ["propto"] = "āˆ", ["scirc"] = "ŝ", ["glj"] = "āŖ¤", ["utilde"] = "Å©", ["sum"] = "āˆ‘", ["bfr"] = "š”Ÿ", ["SucceedsTilde"] = "≿", ["downdownarrows"] = "ā‡Š", ["updownarrow"] = "↕", ["jfr"] = "š”§", ["wopf"] = "š•Ø", ["vellip"] = "ā‹®", ["rfr"] = "š”Æ", ["bigcirc"] = "ā—Æ", ["Zscr"] = "š’µ", ["dHar"] = "ā„„", ["nRightarrow"] = "ā‡", ["deg"] = "°", ["zfr"] = "š”·", ["sung"] = "♪", ["Vscr"] = "š’±", ["Egrav"] = "ƈ", ["leg"] = "ā‹š", ["Sum"] = "āˆ‘", ["Bfr"] = "š”…", ["gnap"] = "⪊", ["ntrianglelefteq"] = "⋬", ["boxUL"] = "ā•", ["simgE"] = "āŖ ", ["frac25"] = "ā…–", ["Rfr"] = "ā„œ", ["NotGreaterGreater"] = "≫̸", ["Zfr"] = "ℨ", ["harr"] = "↔", ["varsupsetneq"] = "āŠ‹ļø€", ["LeftUpDownVector"] = "ā„‘", ["Utilde"] = "ÅØ", ["numsp"] = " ", ["khcy"] = "х", ["nltri"] = "⋪", ["vee"] = "∨", ["lmoustache"] = "āŽ°", ["trpezium"] = "ā¢", ["gscr"] = "ā„Š", ["emptyv"] = "āˆ…", ["ShortRightArrow"] = "→", ["phiv"] = "Ļ•", ["ensp"] = " ", ["NotSquareSupersetEqual"] = "ā‹£", ["jukcy"] = "є", ["angmsdaf"] = "⦭", ["prnsim"] = "⋨", ["uml"] = "ĀØ", ["iocy"] = "ё", ["Omicron"] = "Ο", ["Rightarrow"] = "⇒", ["ncedil"] = "ņ", ["Umacr"] = "ÅŖ", ["TRADE"] = "ā„¢", ["dotsquare"] = "⊔", ["ccedi"] = "Ƨ", ["ltquest"] = "ā©»", ["NewLine"] = "\ -", ["DownLeftVectorBar"] = "ā„–", ["plusacir"] = "⨣", ["egrav"] = "ĆØ", ["SquareSubset"] = "āŠ", ["imagpart"] = "ā„‘", ["rightarrow"] = "→", ["lrcorner"] = "⌟", ["ShortUpArrow"] = "↑", ["Qscr"] = "š’¬", ["glE"] = "āŖ’", ["ltrif"] = "ā—‚", ["rbrkslu"] = "⦐", ["angmsdah"] = "⦯", ["Hcirc"] = "Ĥ", ["boxHu"] = "ā•§", ["fopf"] = "š•—", ["jcy"] = "й", ["Topf"] = "š•‹", ["asympeq"] = "ā‰", ["dash"] = "‐", ["edot"] = "ė", ["rcy"] = "р", ["yen"] = "Ā„", ["orderof"] = "ā„“", ["dagger"] = "†", ["sup"] = "¹", ["Fopf"] = "š”½", ["Bcy"] = "Š‘", ["NotSquareSuperset"] = "⊐̸", ["frac14"] = "¼", ["bull"] = "•", ["cup"] = "∪", ["CloseCurlyDoubleQuote"] = "ā€", ["iiint"] = "∭", ["Rcy"] = "Š ", ["biguplus"] = "⨄", ["verbar"] = "|", ["Zcy"] = "Š—", ["Dscr"] = "š’Ÿ", ["Sup"] = "ā‹‘", ["zeetrf"] = "ℨ", ["HilbertSpace"] = "ā„‹", ["SucceedsEqual"] = "āŖ°", ["bsolhsub"] = "⟈", ["SupersetEqual"] = "āŠ‡", ["aopf"] = "š•’", ["NJcy"] = "Њ", ["Cup"] = "ā‹“", ["TildeFullEqual"] = "≅", ["csube"] = "ā«‘", ["parsim"] = "⫳", ["Ncedil"] = "Ņ", ["top"] = "⊤", ["oelig"] = "œ", ["bsime"] = "ā‹", ["cuwed"] = "ā‹", ["ncongdot"] = "ā©­Ģø", ["bigtriangledown"] = "ā–½", ["varsubsetneq"] = "āŠŠļø€", ["spar"] = "∄", ["CirclePlus"] = "āŠ•", ["subrarr"] = "ℹ", ["LowerRightArrow"] = "ā†˜", ["npar"] = "∦", ["hookrightarrow"] = "↪", ["EmptyVerySmallSquare"] = "ā–«", ["abreve"] = "ă", ["arin"] = "Ć„", ["utrif"] = "ā–“", ["xcap"] = "ā‹‚", ["gsiml"] = "⪐", ["TripleDot"] = "āƒ›", ["cirfnint"] = "⨐", ["larrsim"] = "ℳ", ["imof"] = "⊷", ["shortparallel"] = "∄", ["omicron"] = "Īæ", ["minusd"] = "∸", ["emptyset"] = "āˆ…", ["Wopf"] = "š•Ž", ["boxvr"] = "ā”œ", ["Uuml"] = "Ü", ["xutri"] = "ā–³", ["EqualTilde"] = "≂", ["nge"] = "≱", ["dtri"] = "ā–æ", ["bigotimes"] = "⨂", ["lap"] = "āŖ…", ["ssetmn"] = "āˆ–", ["escr"] = "ℯ", ["LeftRightArrow"] = "↔", ["RightDoubleBracket"] = "⟧", ["Equal"] = "⩵", ["rbrke"] = "⦌", ["nsucceq"] = "āŖ°Ģø", ["uuml"] = "ü", ["ThinSpace"] = " ", ["doteq"] = "≐", ["NotGreaterTilde"] = "≵", ["bkarow"] = "ā¤", ["ocirc"] = "Ć“", ["Gscr"] = "š’¢", ["LowerLeftArrow"] = "↙", ["SuchThat"] = "āˆ‹", ["LeftFloor"] = "⌊", ["GT"] = ">", ["Cacute"] = "Ć", ["swarrow"] = "↙", ["Cross"] = "⨯", ["perp"] = "⊄", ["dzcy"] = "џ", ["lEg"] = "āŖ‹", ["ijlig"] = "ij", ["NotCongruent"] = "≢", ["thickapprox"] = "ā‰ˆ", ["gnE"] = "≩", ["supE"] = "⫆", ["thor"] = "þ", ["lrhar"] = "⇋", ["Ucir"] = "ƛ", ["Otimes"] = "⨷", ["sigmaf"] = "Ļ‚", ["Cedilla"] = "Āø", ["odash"] = "āŠ", ["gesdoto"] = "āŖ‚", ["AMP"] = "&", ["xuplus"] = "⨄", ["theta"] = "Īø", ["Hat"] = "^", ["YUcy"] = "Š®", ["osol"] = "⊘", ["Uring"] = "Å®", ["hookleftarrow"] = "↩", ["zacute"] = "Åŗ", ["lates"] = "āŖ­ļø€", ["RightTriangle"] = "⊳", ["toea"] = "⤨", ["UpperRightArrow"] = "↗", ["ufisht"] = "ℾ", ["lessapprox"] = "āŖ…", ["gnsim"] = "ā‹§", ["dsol"] = "ā§¶", ["upharpoonright"] = "↾", ["Cayleys"] = "ā„­", ["otimes"] = "āŠ—", ["Igrav"] = "Ì", ["piv"] = "Ļ–", ["notin"] = "āˆ‰", ["gdot"] = "Ä”", ["LJcy"] = "Љ", ["thinsp"] = " ", ["RuleDelayed"] = "ā§“", ["utdot"] = "ā‹°", ["swarhk"] = "⤦", ["ltrPar"] = "⦖", ["rhard"] = "⇁", ["nLtv"] = "≪̸", ["DoubleDownArrow"] = "⇓", ["DiacriticalDot"] = "Ė™", ["mcomma"] = "⨩", ["gt"] = ">", ["scsim"] = "≿", ["rcub"] = "}", ["HorizontalLine"] = "─", ["Cconint"] = "∰", ["afr"] = "š”ž", ["rfloor"] = "āŒ‹", ["QUOT"] = "\"", ["nsupE"] = "⫆̸", ["Ocirc"] = "Ɣ", ["ifr"] = "š”¦", ["szli"] = "ß", ["Iopf"] = "š•€", ["qfr"] = "š”®", ["lbrksld"] = "ā¦", ["Gt"] = "≫", ["nsqsube"] = "ā‹¢", ["yfr"] = "š”¶", ["SucceedsSlantEqual"] = "≽", ["rarrhk"] = "↪", ["nprec"] = "āŠ€", ["DiacriticalTilde"] = "˜", ["Afr"] = "š”„", ["frac16"] = "ā…™", ["subsup"] = "ā«“", ["Ifr"] = "ā„‘", ["equiv"] = "≔", ["Agrave"] = "ƀ", ["Equilibrium"] = "ā‡Œ", ["ntilde"] = "Ʊ", ["emacr"] = "ē", ["imath"] = "ı", ["Yfr"] = "š”œ", ["Eogon"] = "Ę", ["CircleMinus"] = "āŠ–", ["looparrowleft"] = "↫", ["iquest"] = "Āæ", ["LeftTeeVector"] = "ℚ", ["sopf"] = "š•¤", ["curvearrowright"] = "↷", ["ntild"] = "Ʊ", ["ecir"] = "ĆŖ", ["bopf"] = "š•“", ["Ecir"] = "Ê", ["MediumSpace"] = " ", ["lesges"] = "āŖ“", ["cemptyv"] = "⦲", ["isin"] = "∈", ["sec"] = "§", ["TildeEqual"] = "ā‰ƒ", ["mapsto"] = "↦", ["xodot"] = "⨀", ["VerticalSeparator"] = "ā˜", ["sqsup"] = "⊐", ["oslash"] = "Ćø", ["setminus"] = "āˆ–", ["ultri"] = "ā—ø", ["bumpe"] = "ā‰", ["csup"] = "⫐", ["uuarr"] = "ā‡ˆ", ["gtrless"] = "≷", ["GreaterLess"] = "≷", ["intcal"] = "⊺", ["Igrave"] = "Ì", ["sqsupseteq"] = "āŠ’", ["Intersection"] = "ā‹‚", ["ecy"] = "э", ["SquareSupersetEqual"] = "āŠ’", ["Uscr"] = "š’°", ["TildeTilde"] = "ā‰ˆ", ["notindot"] = "⋵̸", ["mcy"] = "м", ["divid"] = "Ć·", ["cuesc"] = "ā‹Ÿ", ["Popf"] = "ā„™", ["bNot"] = "ā«­", ["ucy"] = "у", ["isinE"] = "⋹", ["cire"] = "≗", ["agrave"] = "Ć ", ["gtquest"] = "⩼", ["DownArrowUpArrow"] = "⇵", ["Ecy"] = "Š­", ["nvlArr"] = "⤂", ["imped"] = "ʵ", ["Mcy"] = "М", ["preccurlyeq"] = "≼", ["succsim"] = "≿", ["lsh"] = "↰", ["GreaterTilde"] = "≳", ["triminus"] = "⨺", ["Ucy"] = "Š£", ["DoubleLeftArrow"] = "⇐", ["SOFTcy"] = "Ь", ["Proportional"] = "āˆ", ["ofcir"] = "⦿", ["circ"] = "ˆ", ["subsub"] = "ā«•", ["quatint"] = "⨖", ["realpart"] = "ā„œ", ["amp"] = "&", ["cupbrcap"] = "⩈", ["Lsh"] = "↰", ["angrtvb"] = "⊾", ["lvnE"] = "≨︀", ["Ograv"] = "ƒ", ["Gdot"] = "Ä ", ["half"] = "½", ["blk12"] = "ā–’", ["Iota"] = "Ī™", ["AEli"] = "Ɔ", ["squarf"] = "ā–Ŗ", ["cirE"] = "⧃", ["NotSubsetEqual"] = "⊈", ["simdot"] = "⩪", ["Map"] = "⤅", ["Bumpeq"] = "ā‰Ž", ["roplus"] = "⨮", ["langd"] = "⦑", ["Eacut"] = "Ɖ", ["isindot"] = "⋵", ["tbrk"] = "āŽ“", ["xotime"] = "⨂", ["LeftDownTeeVector"] = "ā„”", ["lesdotor"] = "⪃", ["ucirc"] = "Ć»", ["breve"] = "˘", ["Sopf"] = "š•Š", ["cylcty"] = "⌭", ["iogon"] = "ÄÆ", ["sc"] = "≻", ["circledS"] = "ā“ˆ", ["sqsube"] = "āŠ‘", ["rpar"] = ")", ["rrarr"] = "⇉", ["thicksim"] = "∼", ["iota"] = "ι", ["orv"] = "ā©›", ["frac78"] = "ā…ž", ["lfisht"] = "ℼ", ["Sc"] = "āŖ¼", ["blacksquare"] = "ā–Ŗ", ["ShortLeftArrow"] = "←", ["DownArrow"] = "↓", ["gesles"] = "āŖ”", ["dfisht"] = "ℿ", ["sdote"] = "⩦", ["eqvparsl"] = "ā§„", ["roang"] = "⟭", ["ulcorner"] = "⌜", ["jsercy"] = "ј", ["Bopf"] = "š”¹", ["nleqq"] = "≦̸", ["Racute"] = "Ŕ", ["ngE"] = "≧̸", ["LT"] = "<", ["tau"] = "Ļ„", ["bernou"] = "ℬ", ["nvlt"] = "<āƒ’", ["succnsim"] = "ā‹©", ["isinsv"] = "⋳", ["lagran"] = "ā„’", ["rharu"] = "⇀", ["lopf"] = "š•", ["RBarr"] = "⤐", ["acE"] = "∾̳", ["ast"] = "*", ["supne"] = "āŠ‹", ["Acirc"] = "Ƃ", ["chcy"] = "ч", ["crarr"] = "↵", ["vltri"] = "⊲", ["boxplus"] = "āŠž", ["circeq"] = "≗", ["prnE"] = "āŖµ", ["awconint"] = "∳", ["succnapprox"] = "āŖŗ", ["boxvh"] = "┼", ["curvearrowleft"] = "↶", ["hardcy"] = "ъ", ["semi"] = ";", ["yacute"] = "ý", ["ccaps"] = "ā©", ["Ntild"] = "Ƒ", ["barvee"] = "⊽", ["LeftTriangleEqual"] = "⊓", ["npr"] = "āŠ€", ["jcirc"] = "ĵ", ["sqsub"] = "āŠ", ["subedot"] = "⫃", ["Square"] = "ā–”", ["rbrace"] = "}", ["duarr"] = "⇵", ["NotLessGreater"] = "≸", ["disin"] = "⋲", ["rlm"] = "ā€", ["lhard"] = "↽", ["map"] = "↦", ["ap"] = "ā‰ˆ", ["Uacut"] = "Ú", ["OpenCurlyDoubleQuote"] = "ā€œ", ["rarrb"] = "⇄", ["lrtri"] = "⊿", ["olcross"] = "⦻", ["varphi"] = "Ļ•", ["xfr"] = "š”µ", ["nGtv"] = "≫̸", ["supsup"] = "ā«–", ["Eopf"] = "š”¼", ["LeftArrowBar"] = "⇤", ["yum"] = "Ćæ", ["hellip"] = "…", ["Nscr"] = "š’©", ["Hfr"] = "ā„Œ", ["aum"] = "Ƥ", ["mac"] = "ĀÆ", ["pfr"] = "š”­", ["DownArrowBar"] = "⤓", ["ium"] = "ĆÆ", ["emsp"] = "ā€ƒ", ["Xfr"] = "š”›", ["Lt"] = "≪", ["pre"] = "āŖÆ", ["ngeqq"] = "≧̸", ["cudarrr"] = "⤵", ["oopf"] = "š• ", ["fpartint"] = "āØ", ["yacut"] = "ý", ["FilledVerySmallSquare"] = "ā–Ŗ", ["xscr"] = "š“", ["Aum"] = "Ƅ", ["Otilde"] = "ƕ", ["nabla"] = "āˆ‡", ["Pfr"] = "š”“", ["plusdu"] = "⨄", ["Ium"] = "Ə", ["iinfin"] = "⧜", ["eacut"] = "Ć©", ["Agrav"] = "ƀ", ["ye"] = "Ā„", ["emsp13"] = " ", ["tint"] = "∭", ["Iscr"] = "ℐ", ["divideontimes"] = "⋇", ["NotVerticalBar"] = "∤", ["olcir"] = "⦾", ["pertenk"] = "‱", ["ntrianglerighteq"] = "ā‹­", ["mnplus"] = "āˆ“", ["nhArr"] = "ā‡Ž", ["ImaginaryI"] = "ā…ˆ", ["tprime"] = "–", ["rightleftharpoons"] = "ā‡Œ", ["Larr"] = "ā†ž", ["sigma"] = "σ", ["mu"] = "μ", ["ngsim"] = "≵", ["Integral"] = "∫", ["asymp"] = "ā‰ˆ", ["rtrie"] = "⊵", ["sqcap"] = "āŠ“", ["rightsquigarrow"] = "ā†", ["vert"] = "|", ["lcedil"] = "ļ", ["scnsim"] = "ā‹©", ["cen"] = "Ā¢", ["ldquor"] = "ā€ž", ["precneqq"] = "āŖµ", ["Mu"] = "Μ", ["dcy"] = "Š“", ["xvee"] = "⋁", ["COPY"] = "Ā©", ["natural"] = "ā™®", ["Vert"] = "‖", ["lcy"] = "Š»", ["twoheadrightarrow"] = "↠", ["apid"] = "≋", ["tcy"] = "т", ["gescc"] = "āŖ©", ["Hopf"] = "ā„", ["elinters"] = "ā§", ["vnsup"] = "āŠƒāƒ’", ["oacut"] = "ó", ["Dcy"] = "Š”", ["loz"] = "ā—Š", ["Lopf"] = "š•ƒ", ["daleth"] = "ℸ", ["Lcy"] = "Š›", ["scnE"] = "āŖ¶", ["larrfs"] = "ā¤", ["Zcaron"] = "Ž", ["ForAll"] = "āˆ€", ["DoubleUpDownArrow"] = "⇕", ["backprime"] = "‵", ["psi"] = "ψ", ["lnap"] = "āŖ‰", ["profline"] = "āŒ’", ["bot"] = "⊄", ["Therefore"] = "∓", ["frac18"] = "ā…›", ["wr"] = "≀", ["prop"] = "āˆ", ["par"] = "∄", ["bsol"] = "\\", ["nvsim"] = "āˆ¼āƒ’", ["longmapsto"] = "⟼", ["nscr"] = "š“ƒ", ["hfr"] = "š”„", ["Rcaron"] = "Ř", ["epsi"] = "ε", ["sect"] = "§", ["nleqslant"] = "⩽̸", ["lthree"] = "ā‹‹", ["blk14"] = "ā–‘", ["zcaron"] = "ž", ["Tcedil"] = "Å¢", ["rbbrk"] = "ā³", ["lrarr"] = "⇆", ["duhar"] = "ℯ", ["notinE"] = "⋹̸", ["precnsim"] = "⋨", ["rAarr"] = "⇛", ["agrav"] = "Ć ", ["DiacriticalAcute"] = "Ā“", ["cwint"] = "∱", ["napos"] = "ʼn", ["Or"] = "ā©”", ["nedot"] = "≐̸", ["simlE"] = "⪟", ["trie"] = "ā‰œ", ["vDash"] = "⊨", ["LeftTriangle"] = "⊲", ["L"] = "<", ["gg"] = "≫", ["rcaron"] = "ř", ["nparsl"] = "⫽⃄", ["bne"] = "=⃄", ["nsubE"] = "ā«…Ģø", ["RightTeeVector"] = "ā„›", ["Oopf"] = "š•†", ["ETH"] = "Ɛ", ["kgreen"] = "Äø", ["scpolint"] = "⨓", ["LessGreater"] = "≶", ["oror"] = "ā©–", ["sol"] = "/", ["llcorner"] = "āŒž", ["nearhk"] = "⤤", ["Gg"] = "ā‹™", ["DifferentialD"] = "ā…†", ["time"] = "Ɨ", ["VeryThinSpace"] = "ā€Š", ["coprod"] = "∐", ["napid"] = "≋̸", ["RightDownVector"] = "⇂", ["barwed"] = "āŒ…", ["ldrushar"] = "ā„‹", ["orslope"] = "ā©—", ["iexcl"] = "Ā”", ["Longleftrightarrow"] = "⟺", ["boxdl"] = "┐", ["Aacute"] = "Ɓ", ["ltri"] = "ā—ƒ", ["reals"] = "ā„", ["Beta"] = "Ī’", ["plusb"] = "āŠž", ["ET"] = "Ɛ", ["Yacut"] = "Ɲ", ["nesear"] = "⤨", ["Proportion"] = "∷", ["Tilde"] = "∼", ["DJcy"] = "Š‚", ["longleftrightarrow"] = "⟷", ["trisb"] = "ā§", ["OverBracket"] = "āŽ“", ["varpi"] = "Ļ–", ["hopf"] = "š•™", ["supnE"] = "⫌", ["natur"] = "ā™®", ["RightTriangleEqual"] = "⊵", ["supsub"] = "ā«”", ["capdot"] = "ā©€", ["straightepsilon"] = "ϵ", ["PartialD"] = "āˆ‚", ["Aring"] = "ƅ", ["NegativeVeryThinSpace"] = "​", ["subseteqq"] = "ā«…", ["tscy"] = "ц", ["l"] = "<", ["NotGreaterLess"] = "≹", ["UnderBar"] = "_", ["UpArrow"] = "↑", ["lscr"] = "š“", ["smt"] = "āŖŖ", ["eth"] = "ư", ["forall"] = "āˆ€", ["CenterDot"] = "Ā·", ["lfloor"] = "⌊", ["topbot"] = "⌶", ["leftthreetimes"] = "ā‹‹", ["boxv"] = "│", ["boxdR"] = "ā•’", ["RightUpVectorBar"] = "ā„”", ["supsetneq"] = "āŠ‹", ["rightharpoondown"] = "⇁", ["lsime"] = "āŖ", ["rho"] = "ρ", ["gtlPar"] = "⦕", ["NotEqualTilde"] = "≂̸", ["infintie"] = "ā§", ["larrhk"] = "↩", ["prnap"] = "āŖ¹", ["star"] = "ā˜†", ["gfr"] = "š”¤", ["between"] = "≬", ["VDash"] = "⊫", ["ofr"] = "š”¬", ["seArr"] = "ā‡˜", ["preceq"] = "āŖÆ", ["Rho"] = "Ī”", ["NegativeThinSpace"] = "​", ["larr"] = "←", ["kopf"] = "š•œ", ["xsqcup"] = "⨆", ["strns"] = "ĀÆ", ["Rscr"] = "ā„›", ["Gfr"] = "š”Š", ["smashp"] = "⨳", ["GJcy"] = "Ѓ", ["lotimes"] = "⨓", ["nrtri"] = "ā‹«", ["boxVl"] = "ā•¢", ["icirc"] = "Ć®", ["Ofr"] = "š”’", ["circledR"] = "Ā®", ["Ograve"] = "ƒ", ["Wfr"] = "š”š", ["NotLessLess"] = "≪̸", ["Iukcy"] = "І", ["ne"] = "≠", ["centerdot"] = "Ā·", ["sccue"] = "≽", ["boxul"] = "ā”˜", ["NotSucceeds"] = "⊁", ["lmoust"] = "āŽ°", ["supedot"] = "ā«„", ["downarrow"] = "↓", ["ljcy"] = "љ", ["Lcaron"] = "Ľ", ["Barv"] = "ā«§", ["suphsub"] = "ā«—", ["loang"] = "⟬", ["ntlg"] = "≸", ["beta"] = "β", ["malt"] = "✠", ["ascr"] = "š’¶", ["Ouml"] = "Ɩ", ["capbrcup"] = "⩉", ["sub"] = "āŠ‚", ["Ccedil"] = "Ƈ", ["subset"] = "āŠ‚", ["prec"] = "≺", ["igrave"] = "Ƭ", ["bottom"] = "⊄", ["NotTildeEqual"] = "≄", ["andv"] = "⩚", ["laqu"] = "Ā«", ["nwarr"] = "↖", ["cupor"] = "ā©…", ["vsupnE"] = "ā«Œļø€", ["Dagger"] = "—", ["prE"] = "āŖ³", ["integers"] = "ℤ", ["iacute"] = "Ć­", ["pm"] = "±", ["cupcap"] = "⩆", ["ldca"] = "⤶", ["cups"] = "āˆŖļø€", ["Downarrow"] = "⇓", ["frac23"] = "ā…”", ["Iacute"] = "ƍ", ["starf"] = "ā˜…", ["Arin"] = "ƅ", ["rtri"] = "ā–¹", ["DownRightVectorBar"] = "ā„—", ["Updownarrow"] = "⇕", ["mlcp"] = "ā«›", ["Barwed"] = "āŒ†", ["ContourIntegral"] = "∮", ["RightArrow"] = "→", ["prurel"] = "⊰", ["planckh"] = "ā„Ž", ["rect"] = "ā–­", ["efDot"] = "≒", ["eplus"] = "⩱", ["topfork"] = "⫚", ["lBarr"] = "ā¤Ž", ["ffilig"] = "ffi", ["para"] = "¶", ["diamond"] = "ā‹„", ["barwedge"] = "āŒ…", ["supseteqq"] = "⫆", ["bowtie"] = "ā‹ˆ", ["divide"] = "Ć·", ["scedil"] = "ş", ["nwnear"] = "⤧", ["origof"] = "⊶", ["rmoustache"] = "āŽ±", ["Mscr"] = "ℳ", ["backsimeq"] = "ā‹", ["Element"] = "∈", ["subE"] = "ā«…", ["wedgeq"] = "≙", ["percnt"] = "%", ["gla"] = "āŖ„", ["nvrArr"] = "⤃", ["boxUr"] = "ā•™", ["DownLeftVector"] = "↽", ["smeparsl"] = "⧤", ["xmap"] = "⟼", ["nharr"] = "↮", ["otimesas"] = "⨶", ["cupdot"] = "āŠ", ["ffr"] = "š”£", ["nbumpe"] = "ā‰Ģø", ["Oacute"] = "Ɠ", ["ccirc"] = "ĉ", ["lHar"] = "ā„¢", ["Fcy"] = "Ф", ["ocy"] = "о", ["lessdot"] = "ā‹–", ["NotCupCap"] = "≭", ["simne"] = "≆", ["nspar"] = "∦", ["Lstrok"] = "Ł", ["euro"] = "€", ["Longrightarrow"] = "⟹", ["NotExists"] = "āˆ„", ["zopf"] = "š•«", ["NotGreaterEqual"] = "≱", ["boxV"] = "ā•‘", ["ograve"] = "ò", ["harrw"] = "↭", ["jmath"] = "Č·", ["Rarrtl"] = "⤖", ["rarrfs"] = "ā¤ž", ["RightVector"] = "⇀", ["uwangle"] = "⦧", ["RightVectorBar"] = "ā„“", ["nparallel"] = "∦", ["DZcy"] = "Š", ["nltrie"] = "⋬", ["cedil"] = "Āø", ["Gcy"] = "Š“", ["Precedes"] = "≺", ["sce"] = "āŖ°", ["angrt"] = "∟", ["UnderParenthesis"] = "ā", ["ngeqslant"] = "⩾̸", ["nopf"] = "š•Ÿ", ["nleftarrow"] = "ā†š", ["Ocy"] = "Šž", ["precnapprox"] = "āŖ¹", ["sube"] = "āŠ†", ["boxhd"] = "┬", ["bscr"] = "š’·", ["lg"] = "≶", ["dotminus"] = "∸", ["vartriangleright"] = "⊳", ["leqq"] = "≦", ["Ccaron"] = "Č", ["Mellintrf"] = "ℳ", ["nhpar"] = "⫲", ["boxVh"] = "ā•«", ["wscr"] = "š“Œ", ["rharul"] = "ℬ", ["scnap"] = "āŖŗ", ["curlyvee"] = "ā‹Ž", ["in"] = "∈", ["ddotseq"] = "ā©·", ["esim"] = "≂", ["subsetneq"] = "⊊", ["minusdu"] = "⨪", ["Star"] = "⋆", ["nsub"] = "āŠ„", ["Because"] = "∵", ["Jfr"] = "š”", ["RightTriangleBar"] = "⧐", ["circlearrowright"] = "↻", ["Amacr"] = "Ā", ["rbarr"] = "ā¤", ["Jukcy"] = "Š„", ["nsqsupe"] = "ā‹£", ["ShortDownArrow"] = "↓", ["nvgt"] = ">āƒ’", ["fnof"] = "ʒ", ["aacute"] = "Ć”", ["NotRightTriangle"] = "ā‹«", ["lsquo"] = "ā€˜", ["scaron"] = "Å”", ["sqcups"] = "āŠ”ļø€", ["backcong"] = "ā‰Œ", ["rang"] = "⟩", ["hearts"] = "♄", ["larrbfs"] = "⤟", ["omid"] = "⦶", ["iuml"] = "ĆÆ", ["pluse"] = "⩲", ["Kappa"] = "Κ", ["rsquo"] = "’", ["bigtriangleup"] = "ā–³", ["YAcy"] = "ŠÆ", ["kcedil"] = "Ä·", ["iiota"] = "ā„©", ["vsupne"] = "āŠ‹ļø€", ["lAtail"] = "⤛", ["RightCeiling"] = "āŒ‰", ["cularr"] = "↶", ["primes"] = "ā„™", ["Aacut"] = "Ɓ", ["bullet"] = "•", ["LeftUpVectorBar"] = "℘", ["LongLeftRightArrow"] = "⟷", ["backepsilon"] = "϶", ["dscr"] = "š’¹", ["frac1"] = "½", ["epsiv"] = "ϵ", ["male"] = "♂", ["lcaron"] = "ľ", ["nsimeq"] = "≄", ["andand"] = "ā©•", ["Kcedil"] = "Ķ", ["lsqb"] = "[", ["Scedil"] = "Ş", ["boxDr"] = "ā•“", ["hksearow"] = "⤄", ["sqsubset"] = "āŠ", ["digamma"] = "Ļ", ["ThickSpace"] = "āŸā€Š", ["bnot"] = "⌐", ["Pscr"] = "š’«", ["SquareUnion"] = "āŠ”", ["Chi"] = "Χ", ["NotDoubleVerticalBar"] = "∦", ["lparlt"] = "⦓", ["Vbar"] = "ā««", ["gjcy"] = "ѓ", ["die"] = "ĀØ", ["oS"] = "ā“ˆ", ["because"] = "∵", ["NotEqual"] = "≠", ["gcy"] = "г", ["xoplus"] = "⨁", ["thetav"] = "Ļ‘", ["bigodot"] = "⨀", ["equals"] = "=", ["parsl"] = "⫽", ["Ccedi"] = "Ƈ", ["nlt"] = "≮", ["LongRightArrow"] = "⟶", ["uopf"] = "š•¦", ["race"] = "∽̱", ["DoubleLongLeftArrow"] = "⟸", ["solbar"] = "⌿", ["ac"] = "∾", ["angrtvbd"] = "ā¦", ["imagline"] = "ℐ", ["ulcrop"] = "āŒ", ["vrtri"] = "⊳", ["nprcue"] = "ā‹ ", ["Kopf"] = "š•‚", ["supsetneqq"] = "⫌", ["ic"] = "⁣", ["nsupseteq"] = "āŠ‰", ["Verbar"] = "‖", ["fcy"] = "ф", ["lesseqqgtr"] = "āŖ‹", ["gesl"] = "⋛︀", ["Jsercy"] = "Ј", ["iecy"] = "е", ["leftharpoondown"] = "↽", ["times"] = "Ɨ", ["NotGreater"] = "≯", ["prcue"] = "≼", ["cap"] = "∩", ["rtrif"] = "ā–ø", ["Otild"] = "ƕ", ["Omega"] = "Ī©", ["ntriangleleft"] = "⋪", ["rBarr"] = "ā¤", ["frac34"] = "¾", ["szlig"] = "ß", ["pointint"] = "⨕", ["Sacute"] = "Ś", ["Hacek"] = "ˇ", ["Iuml"] = "Ə", ["Hstrok"] = "Ħ", ["af"] = "⁔", ["cedi"] = "Āø", ["curlyeqsucc"] = "ā‹Ÿ", ["notinvb"] = "ā‹·", ["LeftArrow"] = "←", ["Uogon"] = "Ų", ["ApplyFunction"] = "⁔", ["nexists"] = "āˆ„", ["rarrw"] = "ā†", ["Uopf"] = "š•Œ", ["sqcaps"] = "āŠ“ļø€", ["Darr"] = "↔", ["square"] = "ā–”", ["no"] = "¬", ["KJcy"] = "Ќ", ["DoubleLongLeftRightArrow"] = "⟺", ["bigoplus"] = "⨁", ["ang"] = "∠", ["isins"] = "ā‹“", ["Cap"] = "ā‹’", ["ldrdhar"] = "ā„§", ["minusb"] = "⊟", ["DownBreve"] = "Ģ‘", ["hArr"] = "⇔", ["udhar"] = "ā„®", ["nrArr"] = "ā‡", ["shchcy"] = "щ", ["NotLessEqual"] = "≰", ["SHcy"] = "ŠØ", ["plankv"] = "ā„", ["poun"] = "Ā£", ["lvertneqq"] = "≨︀", ["dopf"] = "š••", ["varnothing"] = "āˆ…", ["succapprox"] = "āŖø", ["caret"] = "⁁", ["nwarrow"] = "↖", ["Exists"] = "∃", ["varsubsetneqq"] = "ā«‹ļø€", ["Ccirc"] = "Ĉ", ["lsimg"] = "āŖ", ["Yacute"] = "Ɲ", ["nLl"] = "ā‹˜Ģø", ["racute"] = "ŕ", ["leftrightsquigarrow"] = "↭", ["xcup"] = "ā‹ƒ", ["leftrightarrow"] = "↔", ["supmult"] = "ā«‚", ["LeftUpTeeVector"] = "ā„ ", ["UnderBrace"] = "āŸ", ["varr"] = "↕", ["PrecedesTilde"] = "≾", ["gcirc"] = "ĝ", ["diams"] = "♦", ["geq"] = "≄", ["rhov"] = "ϱ", ["phi"] = "φ", ["Superset"] = "⊃", ["DownLeftTeeVector"] = "ā„ž", ["Sigma"] = "Ī£", ["lsaquo"] = "‹", ["NotHumpDownHump"] = "ā‰ŽĢø", ["triangleq"] = "ā‰œ", ["hstrok"] = "ħ", ["gesdot"] = "āŖ€", ["risingdotseq"] = "≓", ["luruhar"] = "Ω", ["lbrack"] = "[", ["varepsilon"] = "ϵ", ["nrarrc"] = "⤳̸", ["DownTeeArrow"] = "↧", ["nvDash"] = "⊭", ["Euml"] = "Ƌ", ["it"] = "⁢", ["homtht"] = "∻", ["els"] = "āŖ•", ["elsdot"] = "āŖ—", ["nges"] = "⩾̸", ["rarrc"] = "⤳", ["Subset"] = "⋐", ["cirscir"] = "ā§‚", ["YIcy"] = "Ї", ["Leftrightarrow"] = "⇔", ["Cfr"] = "ā„­", ["rightarrowtail"] = "↣", ["quest"] = "?", ["telrec"] = "āŒ•", ["Wedge"] = "ā‹€", ["ouml"] = "ƶ", ["brvbar"] = "¦", ["Sscr"] = "š’®", ["planck"] = "ā„", ["scE"] = "āŖ“", ["Wscr"] = "š’²", ["xharr"] = "⟷", ["rlhar"] = "ā‡Œ", ["Vvdash"] = "⊪", ["circledast"] = "āŠ›", ["gvertneqq"] = "≩︀", ["varsigma"] = "Ļ‚", ["realine"] = "ā„›", ["ltrie"] = "⊓", ["precapprox"] = "āŖ·", ["Re"] = "ā„œ", ["prod"] = "āˆ", ["ograv"] = "ò", ["maltese"] = "✠", ["Ffr"] = "š”‰", ["ropar"] = "⦆", ["NegativeThickSpace"] = "​", ["Tscr"] = "š’Æ", ["ccedil"] = "Ƨ", ["frown"] = "⌢", ["nlsim"] = "≓", ["midcir"] = "ā«°", ["nang"] = "āˆ āƒ’", ["excl"] = "!", ["supdsub"] = "⫘", ["dharl"] = "ā‡ƒ", ["boxHd"] = "╤", ["Zopf"] = "ℤ", ["Longleftarrow"] = "⟸", ["dbkarow"] = "ā¤", ["scy"] = "с", ["gE"] = "≧", ["hkswarow"] = "⤦", ["el"] = "āŖ™", ["copysr"] = "ā„—", ["Ucirc"] = "ƛ", ["acirc"] = "Ć¢", ["popf"] = "š•”", ["frac35"] = "ā…—", ["npart"] = "āˆ‚Ģø", ["lbrke"] = "⦋", ["Tcaron"] = "Ť", ["aleph"] = "ℵ", ["AM"] = "&", ["THORN"] = "ƞ", ["varkappa"] = "ϰ", ["rtimes"] = "ā‹Š", ["gtreqless"] = "ā‹›", ["yucy"] = "ю", ["lharu"] = "↼", ["leftharpoonup"] = "↼", ["ClockwiseContourIntegral"] = "∲", ["boxUR"] = "ā•š", ["Xopf"] = "š•", ["brvba"] = "¦", ["boxVr"] = "ā•Ÿ", ["wp"] = "ā„˜", ["doteqdot"] = "≑", ["spadesuit"] = "ā™ ", ["ell"] = "ā„“", ["longleftarrow"] = "⟵", ["uarr"] = "↑", ["blacktriangleright"] = "ā–ø", ["Sub"] = "⋐", ["subdot"] = "āŖ½", ["urtri"] = "ā—¹", ["naturals"] = "ā„•", ["langle"] = "⟨", ["supset"] = "⊃", ["Breve"] = "˘", ["NotSquareSubsetEqual"] = "ā‹¢", ["notnivb"] = "⋾", ["downharpoonright"] = "⇂", ["sime"] = "ā‰ƒ", ["bprime"] = "‵", ["ngtr"] = "≯", ["succneqq"] = "āŖ¶", ["notnivc"] = "⋽", ["phmmat"] = "ℳ", ["eparsl"] = "ā§£", ["macr"] = "ĀÆ", ["aring"] = "Ć„", ["gesdotol"] = "āŖ„", ["sdot"] = "ā‹…", ["RightArrowBar"] = "⇄", ["Pcy"] = "П", ["clubs"] = "♣", ["Vdashl"] = "⫦", ["Ugrave"] = "ƙ", ["or"] = "∨", ["nleq"] = "≰", ["curarrm"] = "⤼", ["NestedGreaterGreater"] = "≫", ["mstpos"] = "∾", ["awint"] = "⨑", ["nVdash"] = "⊮", ["NotSquareSubset"] = "āŠĢø", ["UnderBracket"] = "āŽµ", ["rightrightarrows"] = "⇉", ["IOcy"] = "Ё", ["zcy"] = "Š·", ["lowast"] = "āˆ—", ["nfr"] = "š”«", ["exponentiale"] = "ā…‡", ["aeli"] = "Ʀ", ["RightArrowLeftArrow"] = "⇄", ["raemptyv"] = "⦳", ["zhcy"] = "ж", ["uHar"] = "ā„£", ["spades"] = "ā™ ", ["vfr"] = "š”³", ["oacute"] = "ó", ["Atilde"] = "ƃ", ["sim"] = "∼", ["swnwar"] = "⤪", ["eqsim"] = "≂", ["ExponentialE"] = "ā…‡", ["oum"] = "ƶ", ["IJlig"] = "IJ", ["questeq"] = "ā‰Ÿ", ["Zdot"] = "Å»", ["rsaquo"] = "›", ["rsh"] = "↱", ["yicy"] = "ї", ["gopf"] = "š•˜", ["caps"] = "āˆ©ļø€", ["lurdshar"] = "ℊ", ["ordm"] = "Āŗ", ["cir"] = "ā—‹", ["nvltrie"] = "āŠ“āƒ’", ["lcub"] = "{", ["lt"] = "<", ["circlearrowleft"] = "↺", ["boxDL"] = "ā•—", ["Nfr"] = "š”‘", ["ge"] = "≄", ["LeftUpVector"] = "↿", ["ord"] = "ĀŖ", ["PrecedesSlantEqual"] = "≼", ["Supset"] = "ā‹‘", ["nwarhk"] = "⤣", ["angsph"] = "∢", ["Vfr"] = "š”™", ["part"] = "āˆ‚", ["smallsetminus"] = "āˆ–", ["sqsupset"] = "⊐", ["Gcirc"] = "Ĝ", ["Gopf"] = "š”¾", ["raqu"] = "Ā»", ["Oum"] = "Ɩ", ["UpTeeArrow"] = "ↄ", ["ngt"] = "≯", ["boxHD"] = "╦", ["diam"] = "ā‹„", ["npre"] = "āŖÆĢø", ["ddarr"] = "ā‡Š", ["quaternions"] = "ā„", ["Union"] = "ā‹ƒ", ["nexist"] = "āˆ„", ["ccaron"] = "č", ["itilde"] = "Ä©", ["rarrlp"] = "↬", ["subnE"] = "ā«‹", ["gtrarr"] = "ℸ", ["pluscir"] = "⨢", ["apacir"] = "⩯", ["NotElement"] = "āˆ‰", ["bemptyv"] = "⦰", ["rbrack"] = "]", ["slarr"] = "←", ["iques"] = "Āæ", ["Rrightarrow"] = "⇛", ["Omacr"] = "Ō", ["Ascr"] = "š’œ", ["swarr"] = "↙", ["NotSupersetEqual"] = "āŠ‰", ["atilde"] = "Ć£", ["Emacr"] = "Ē", ["ni"] = "āˆ‹", ["am"] = "&", ["nshortparallel"] = "∦", ["Vee"] = "⋁", ["rcedil"] = "ŗ", ["gvnE"] = "≩︀", ["bigvee"] = "⋁", ["female"] = "♀", ["sfrown"] = "⌢", ["um"] = "ĀØ", ["Qfr"] = "š””", ["uharr"] = "↾", ["FilledSmallSquare"] = "ā—¼", ["varrho"] = "ϱ", ["dtrif"] = "ā–¾", ["uacute"] = "Ćŗ", ["NotPrecedes"] = "āŠ€", ["upuparrows"] = "ā‡ˆ", ["KHcy"] = "Š„", ["heartsuit"] = "♄", ["seswar"] = "⤩", ["bcy"] = "б", ["acir"] = "Ć¢", ["Fscr"] = "ℱ", ["Xscr"] = "š’³", ["Im"] = "ā„‘", ["Ubrcy"] = "ŠŽ", ["amalg"] = "⨿", ["fjlig"] = "fj", ["mapstoup"] = "ↄ", ["Acir"] = "Ƃ", ["lAarr"] = "ā‡š", ["loarr"] = "⇽", ["Mopf"] = "š•„", ["neArr"] = "⇗", ["kappa"] = "Īŗ", ["uhblk"] = "ā–€", ["LeftCeiling"] = "⌈", ["middo"] = "Ā·", ["ReverseEquilibrium"] = "⇋", ["UpArrowDownArrow"] = "⇅", ["plusmn"] = "±", ["nsim"] = "≁", ["rArr"] = "⇒", ["emsp14"] = " ", ["lozf"] = "ā§«", ["nle"] = "≰", ["andd"] = "⩜", ["egs"] = "āŖ–", ["RoundImplies"] = "ā„°", ["rlarr"] = "⇄", ["lobrk"] = "⟦", ["nsube"] = "⊈", ["uharl"] = "↿", ["lmidot"] = "ŀ", ["DotEqual"] = "≐", ["lnapprox"] = "āŖ‰", ["period"] = ".", ["ccupssm"] = "⩐", ["equivDD"] = "⩸", ["boxtimes"] = "⊠", ["vopf"] = "š•§", ["ncy"] = "н", ["yopf"] = "š•Ŗ", ["boxminus"] = "⊟", ["scap"] = "āŖø", ["hybull"] = "⁃", ["profalar"] = "⌮", ["xlArr"] = "⟸", ["ring"] = "˚", ["vcy"] = "в", ["oscr"] = "ā„“", ["bcong"] = "ā‰Œ", ["tstrok"] = "ŧ", ["downharpoonleft"] = "ā‡ƒ", ["NotLessSlantEqual"] = "⩽̸", ["oint"] = "∮", ["bdquo"] = "ā€ž", ["rAtail"] = "⤜", ["Laplacetrf"] = "ā„’", ["fscr"] = "š’»", ["Theta"] = "Θ", ["CircleTimes"] = "āŠ—", ["DoubleLeftTee"] = "⫤", ["rdquor"] = "ā€", ["gel"] = "ā‹›", ["prsim"] = "≾", ["dwangle"] = "⦦", ["Imacr"] = "ÄŖ", ["UpTee"] = "⊄", ["Tau"] = "Τ", ["hcirc"] = "Ä„", ["srarr"] = "→", ["jscr"] = "š’æ", ["Ncy"] = "Š", ["opar"] = "⦷", ["Gbreve"] = "Ğ", ["eDot"] = "≑", ["Uarr"] = "ā†Ÿ", ["QUO"] = "\"", ["EmptySmallSquare"] = "ā—»", ["curre"] = "¤", ["Vcy"] = "Š’", ["laemptyv"] = "⦓", ["Jcy"] = "Š™", ["gsime"] = "āŖŽ", ["apos"] = "'", ["Edot"] = "Ė", ["commat"] = "@", ["yuml"] = "Ćæ", ["RightFloor"] = "āŒ‹", ["Coproduct"] = "∐", ["boxdr"] = "ā”Œ", ["ssmile"] = "⌣", ["lesseqgtr"] = "ā‹š", ["hamilt"] = "ā„‹", ["notniva"] = "∌", ["rthree"] = "ā‹Œ", ["iff"] = "⇔", ["longrightarrow"] = "⟶", ["thksim"] = "∼", ["Diamond"] = "ā‹„", ["ohbar"] = "⦵", ["searr"] = "ā†˜", ["uplus"] = "āŠŽ", ["otild"] = "Ƶ", ["angmsd"] = "∔", ["oplus"] = "āŠ•", ["Sfr"] = "š”–", ["nsime"] = "≄", ["nvdash"] = "⊬", ["UpEquilibrium"] = "ā„®", ["middot"] = "Ā·", ["Rcedil"] = "Ŗ", ["eopf"] = "š•–", ["euml"] = "Ć«", ["Epsilon"] = "Ī•", ["dot"] = "Ė™", ["boxVH"] = "╬", ["nGg"] = "⋙̸", ["rdca"] = "⤷", ["THOR"] = "ƞ", ["nldr"] = " ", ["rightleftarrows"] = "⇄", ["ltimes"] = "⋉", ["supe"] = "āŠ‡", ["frac38"] = "ā…œ", ["zwj"] = "ā€", ["nsucc"] = "⊁", ["GreaterEqual"] = "≄", ["circledcirc"] = "⊚", ["Scaron"] = "Å ", ["Ntilde"] = "Ƒ", ["NotRightTriangleBar"] = "⧐̸", ["parallel"] = "∄", ["dcaron"] = "ď", ["nLt"] = "ā‰Ŗāƒ’", ["VerticalLine"] = "|", ["Ubreve"] = "Ŭ", ["sup1"] = "¹", ["Dcaron"] = "Ď", ["Psi"] = "ĪØ", ["ovbar"] = "⌽", ["xwedge"] = "ā‹€", ["rarrtl"] = "↣", ["ange"] = "⦤", ["LeftVector"] = "↼", ["nsupset"] = "āŠƒāƒ’", ["supdot"] = "āŖ¾", ["Dot"] = "ĀØ", ["rdldhar"] = "ā„©", ["blank"] = "␣", ["kcy"] = "Šŗ", ["numero"] = "ā„–", ["DoubleDot"] = "ĀØ", ["tdot"] = "āƒ›", ["hyphen"] = "‐", ["roarr"] = "⇾", ["LongLeftArrow"] = "⟵", ["frac12"] = "½", ["NotPrecedesEqual"] = "āŖÆĢø", ["oast"] = "āŠ›", ["rightthreetimes"] = "ā‹Œ", ["leftrightharpoons"] = "⇋", ["Nacute"] = "Ń", ["wfr"] = "š”“", ["cuepr"] = "ā‹ž", ["Conint"] = "∯", ["boxvR"] = "ā•ž", ["Gamma"] = "Ī“", ["nvge"] = "ā‰„āƒ’", ["UpperLeftArrow"] = "↖", ["wedbar"] = "⩟", ["dharr"] = "⇂", ["plussim"] = "⨦", ["ltcc"] = "āŖ¦", ["Pi"] = "Ī ", ["angmsdaa"] = "⦨", ["SquareIntersection"] = "āŠ“", ["Cdot"] = "Ċ", ["beth"] = "ā„¶", ["nsupseteqq"] = "⫆̸", ["dArr"] = "⇓", ["LeftTriangleBar"] = "ā§", ["hslash"] = "ā„", ["delta"] = "Ī“", ["pscr"] = "š“…", ["vangrt"] = "⦜", ["gacute"] = "ǵ", ["vBarv"] = "ā«©", ["boxDR"] = "ā•”", ["dlcrop"] = "āŒ", ["lceil"] = "⌈", ["ltlarr"] = "ā„¶", ["upharpoonleft"] = "↿", ["uring"] = "ÅÆ", ["gammad"] = "Ļ", ["tscr"] = "š“‰", ["bsim"] = "∽", ["LeftTeeArrow"] = "↤", ["et"] = "ư", ["lne"] = "āŖ‡", ["ubreve"] = "Å­", ["DotDot"] = "⃜", ["tcaron"] = "Å„", ["UpArrowBar"] = "⤒", ["ucir"] = "Ć»", ["nsccue"] = "ā‹”", ["Aopf"] = "š”ø", ["DownRightVector"] = "⇁", ["cfr"] = "š” ", ["angle"] = "∠", ["simeq"] = "ā‰ƒ", ["eg"] = "⪚", ["DiacriticalGrave"] = "`", ["Backslash"] = "āˆ–", ["eogon"] = "ę", ["erarr"] = "ℱ", ["ee"] = "ā…‡", ["models"] = "⊧", ["csub"] = "ā«", ["inodot"] = "ı", ["NotRightTriangleEqual"] = "ā‹­", ["dashv"] = "⊣", ["gimel"] = "ā„·", ["nvap"] = "ā‰āƒ’", ["ecirc"] = "ĆŖ", ["siml"] = "āŖ", ["and"] = "∧", ["dollar"] = "$", ["qscr"] = "š“†", ["submult"] = "⫁", ["cent"] = "Ā¢", ["rnmid"] = "ā«®", ["LeftTee"] = "⊣", ["qopf"] = "š•¢", ["rsqb"] = "]", ["bbrktbrk"] = "āŽ¶", ["expectation"] = "ā„°", ["njcy"] = "њ", ["djcy"] = "ђ", ["profsurf"] = "āŒ“", ["boxHU"] = "ā•©", ["bumpeq"] = "ā‰", ["boxvH"] = "╪", ["GreaterEqualLess"] = "ā‹›", ["lneqq"] = "≨", ["xnis"] = "ā‹»", ["olt"] = "ā§€", ["minus"] = "āˆ’", ["les"] = "⩽", ["xlarr"] = "⟵", ["colone"] = "≔", ["larrpl"] = "⤹", ["triangledown"] = "ā–æ", ["lnE"] = "≨", ["simrarr"] = "Ⅎ", ["comp"] = "∁", ["angmsdae"] = "⦬", ["LessLess"] = "āŖ”", ["And"] = "ā©“", ["forkv"] = "ā«™", ["NotNestedLessLess"] = "⪔̸", ["ReverseUpEquilibrium"] = "ℯ", ["rotimes"] = "⨵", ["RightAngleBracket"] = "⟩", ["nbsp"] = "Ā ", ["smtes"] = "⪬︀", ["Lcedil"] = "Ä»", ["nearr"] = "↗", ["searrow"] = "ā†˜", ["napE"] = "ā©°Ģø", ["drbkarow"] = "⤐", ["iopf"] = "š•š", ["Not"] = "⫬", ["Upsilon"] = "Ī„", ["Ecaron"] = "Ě", ["ncup"] = "ā©‚", ["zwnj"] = "ā€Œ", ["ominus"] = "āŠ–", ["Tstrok"] = "Ŧ", ["Tcy"] = "Š¢", ["vprop"] = "āˆ", ["lat"] = "āŖ«", ["DoubleLeftRightArrow"] = "⇔", ["OverParenthesis"] = "āœ", ["DScy"] = "Š…", ["doublebarwedge"] = "āŒ†", ["Itilde"] = "ÄØ", ["puncsp"] = "ā€ˆ", ["bigsqcup"] = "⨆", ["nvHarr"] = "⤄", ["Oslash"] = "Ƙ", ["bnequiv"] = "ā‰”āƒ„", ["vartriangleleft"] = "⊲", ["rscr"] = "š“‡", ["umacr"] = "Å«", ["raquo"] = "Ā»", ["nGt"] = "ā‰«āƒ’", ["tcedil"] = "Å£", ["demptyv"] = "⦱", ["backsim"] = "∽", ["triplus"] = "⨹", ["mldr"] = "…", ["UpDownArrow"] = "↕", ["rangle"] = "⟩", ["topcir"] = "⫱", ["RightDownVectorBar"] = "ā„•", ["ntriangleright"] = "ā‹«", ["iiiint"] = "⨌", ["smid"] = "∣", ["Dfr"] = "š”‡", ["Eacute"] = "Ɖ", ["order"] = "ā„“", ["larrb"] = "⇤", ["NotTildeFullEqual"] = "≇", ["Colone"] = "ā©“", ["Abreve"] = "Ă", ["otilde"] = "Ƶ", ["Escr"] = "ā„°", ["Vopf"] = "š•", ["twoheadleftarrow"] = "ā†ž", ["cuvee"] = "ā‹Ž", ["xrArr"] = "⟹", ["NotSucceedsTilde"] = "≿̸", ["Gammad"] = "Ϝ", ["NotSuperset"] = "āŠƒāƒ’", ["LeftAngleBracket"] = "⟨", ["Zacute"] = "Ź", ["Uarrocir"] = "℉", ["Alpha"] = "Ī‘", ["ratail"] = "⤚", ["nacute"] = "ń", ["DownTee"] = "⊤", ["boxur"] = "ā””", ["Efr"] = "š”ˆ", ["conint"] = "∮", ["rightharpoonup"] = "⇀", ["DownRightTeeVector"] = "℟", ["blacktriangledown"] = "ā–¾", ["boxVL"] = "ā•£", ["vBar"] = "⫨", ["LessSlantEqual"] = "⩽", ["sacute"] = "ś", ["SquareSubsetEqual"] = "āŠ‘", ["uArr"] = "⇑", ["efr"] = "š”¢", ["odiv"] = "⨸", ["iscr"] = "š’¾", ["rx"] = "ā„ž", ["infin"] = "āˆž", ["ncong"] = "≇", ["lE"] = "≦", ["ldquo"] = "ā€œ"} \ No newline at end of file diff --git a/mods/modlib/web/uri.lua b/mods/modlib/web/uri.lua deleted file mode 100644 index 87e6aca2..00000000 --- a/mods/modlib/web/uri.lua +++ /dev/null @@ -1,42 +0,0 @@ --- URI escaping utilities --- See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI - -local uri_unescaped_chars = {} -for char in ("-_.!~*'()"):gmatch(".") do - uri_unescaped_chars[char] = true -end -local function add_unescaped_range(from, to) - for byte = from:byte(), to:byte() do - uri_unescaped_chars[string.char(byte)] = true - end -end -add_unescaped_range("0", "9") -add_unescaped_range("a", "z") -add_unescaped_range("A", "Z") - -local uri_allowed_chars = table.copy(uri_unescaped_chars) -for char in (";,/?:@&=+$#"):gmatch(".") do - -- Reserved characters are allowed - uri_allowed_chars[char] = true -end - -local function encode(text, allowed_chars) - return text:gsub(".", function(char) - if allowed_chars[char] then - return char - end - return ("%%%02X"):format(char:byte()) - end) -end - -local uri = {} - -function uri.encode_component(text) - return encode(text, uri_unescaped_chars) -end - -function uri.encode(text) - return encode(text, uri_allowed_chars) -end - -return uri \ No newline at end of file diff --git a/mods/player_api b/mods/player_api new file mode 160000 index 00000000..64da3ede --- /dev/null +++ b/mods/player_api @@ -0,0 +1 @@ +Subproject commit 64da3edef46ad77907dbef0410556410fe77e7be diff --git a/mods/player_api/README.txt b/mods/player_api/README.txt deleted file mode 100644 index 37afadfa..00000000 --- a/mods/player_api/README.txt +++ /dev/null @@ -1,27 +0,0 @@ -Minetest Game mod: player_api -============================= -See license.txt for license information. - -Provides an API to allow multiple mods to set player models and textures. -Also sets the default model, texture, and player flags. -This mod is only for content related to the Player API and the player object. - -Authors of source code ----------------------- -Originally by celeron55, Perttu Ahola (LGPLv2.1+) -Various Minetest developers and contributors (LGPLv2.1+) - -Authors of media (textures, models and sounds) ----------------------------------------------- -Original model by MirceaKitsune (CC BY-SA 3.0). -Various alterations and fixes by kilbith, sofar, xunto, Rogier-5, TeTpaAka, Desour, -stujones11, An0n3m0us (CC BY-SA 3.0): - character.b3d - character.blend - -Jordach (CC BY-SA 3.0): - character.png - -celeron55, Perttu Ahola (CC BY-SA 3.0): - player.png - player_back.png diff --git a/mods/player_api/api.lua b/mods/player_api/api.lua deleted file mode 100644 index 6a572f55..00000000 --- a/mods/player_api/api.lua +++ /dev/null @@ -1,239 +0,0 @@ -player_api = {} - --- Player animation blending --- Note: This is currently broken due to a bug in Irrlicht, leave at 0 -local animation_blend = 0 - -player_api.registered_models = {} - --- Local for speed. -local models = player_api.registered_models - -local function collisionbox_equals(collisionbox, other_collisionbox) - if collisionbox == other_collisionbox then - return true - end - for index = 1, 6 do - if collisionbox[index] ~= other_collisionbox[index] then - return false - end - end - return true -end - -function player_api.register_model(name, def) - models[name] = def - def.visual_size = def.visual_size or {x = 1, y = 1} - def.collisionbox = def.collisionbox or {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3} - def.stepheight = def.stepheight or 0.6 - def.eye_height = def.eye_height or 1.47 - - -- Sort animations into property classes: - -- Animations with same properties have the same _equals value - for animation_name, animation in pairs(def.animations) do - animation.eye_height = animation.eye_height or def.eye_height - animation.collisionbox = animation.collisionbox or def.collisionbox - animation.override_local = animation.override_local or false - - for _, other_animation in pairs(def.animations) do - if other_animation._equals then - if collisionbox_equals(animation.collisionbox, other_animation.collisionbox) - and animation.eye_height == other_animation.eye_height then - animation._equals = other_animation._equals - break - end - end - end - animation._equals = animation._equals or animation_name - end -end - --- Player stats and animations --- model, textures, animation -local players = {} -player_api.player_attached = {} - -local function get_player_data(player) - return assert(players[player:get_player_name()]) -end - -function player_api.get_animation(player) - return get_player_data(player) -end - --- Called when a player's appearance needs to be updated -function player_api.set_model(player, model_name) - local player_data = get_player_data(player) - if player_data.model == model_name then - return - end - -- Update data - player_data.model = model_name - -- Clear animation data as the model has changed - -- (required for setting the `stand` animation not to be a no-op) - player_data.animation, player_data.animation_speed, player_data.animation_loop = nil, nil, nil - - local model = models[model_name] - if model then - player:set_properties({ - mesh = model_name, - textures = player_data.textures or model.textures, - visual = "mesh", - visual_size = model.visual_size, - stepheight = model.stepheight - }) - -- sets local_animation, collisionbox & eye_height - player_api.set_animation(player, "stand") - else - player:set_properties({ - textures = {"player.png", "player_back.png"}, - visual = "upright_sprite", - visual_size = {x = 1, y = 2}, - collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.75, 0.3}, - stepheight = 0.6, - eye_height = 1.625, - }) - end -end - -function player_api.get_textures(player) - local player_data = get_player_data(player) - local model = models[player_data.model] - return assert(player_data.textures or (model and model.textures)) -end - -function player_api.set_textures(player, textures) - local player_data = get_player_data(player) - local model = models[player_data.model] - local new_textures = assert(textures or (model and model.textures)) - player_data.textures = new_textures - player:set_properties({textures = new_textures}) -end - -function player_api.set_texture(player, index, texture) - local textures = table.copy(player_api.get_textures(player)) - textures[index] = texture - player_api.set_textures(player, textures) -end - -function player_api.set_animation(player, anim_name, speed, loop) - local player_data = get_player_data(player) - local model = models[player_data.model] - if not (model and model.animations[anim_name]) then - return - end - speed = speed or model.animation_speed - if loop == nil then - loop = true - end - if player_data.animation == anim_name - and player_data.animation_speed == speed - and player_data.animation_loop == loop - then - return - end - local previous_anim = model.animations[player_data.animation] or {} - local anim = model.animations[anim_name] - player_data.animation = anim_name - player_data.animation_speed = speed - player_data.animation_loop = loop - -- If necessary change the local animation (only seen by the client of *that* player) - -- `override_local` <=> suspend local animations while this one is active - -- (this is basically a hack, proper engine feature needed...) - if anim.override_local ~= previous_anim.override_local then - if anim.override_local then - local none = {x=0, y=0} - player:set_local_animation(none, none, none, none, 1) - else - local a = model.animations -- (not specific to the animation being set) - player:set_local_animation( - a.stand, a.walk, a.mine, a.walk_mine, - model.animation_speed or 30 - ) - end - end - -- Set the animation seen by everyone else - player:set_animation(anim, speed, animation_blend, loop) - -- Update related properties if they changed - if anim._equals ~= previous_anim._equals then - player:set_properties({ - collisionbox = anim.collisionbox, - eye_height = anim.eye_height - }) - end -end - -minetest.register_on_joinplayer(function(player) - local name = player:get_player_name() - players[name] = {} - player_api.player_attached[name] = false -end) - -minetest.register_on_leaveplayer(function(player) - local name = player:get_player_name() - players[name] = nil - player_api.player_attached[name] = nil -end) - --- Localize for better performance. -local player_set_animation = player_api.set_animation -local player_attached = player_api.player_attached - --- Prevent knockback for attached players -local old_calculate_knockback = minetest.calculate_knockback -function minetest.calculate_knockback(player, ...) - if player_attached[player:get_player_name()] then - return 0 - end - return old_calculate_knockback(player, ...) -end - --- Check each player and apply animations -function player_api.globalstep() - for _, player in ipairs(minetest.get_connected_players()) do - local name = player:get_player_name() - local player_data = players[name] - local model = player_data and models[player_data.model] - if model and not player_attached[name] then - local controls = player:get_player_control() - local animation_speed_mod = model.animation_speed or 30 - - -- Determine if the player is sneaking, and reduce animation speed if so - if controls.sneak then - animation_speed_mod = animation_speed_mod / 2 - end - - -- Apply animations based on what the player is doing - if player:get_hp() == 0 then - player_set_animation(player, "lay") - elseif controls.up or controls.down or controls.left or controls.right then - if controls.LMB or controls.RMB then - player_set_animation(player, "walk_mine", animation_speed_mod) - else - player_set_animation(player, "walk", animation_speed_mod) - end - elseif controls.LMB or controls.RMB then - player_set_animation(player, "mine", animation_speed_mod) - else - player_set_animation(player, "stand", animation_speed_mod) - end - end - end -end - --- Mods can modify the globalstep by overriding player_api.globalstep -minetest.register_globalstep(function(...) - player_api.globalstep(...) -end) - -for _, api_function in pairs({"get_animation", "set_animation", "set_model", "set_textures"}) do - local original_function = player_api[api_function] - player_api[api_function] = function(player, ...) - if not players[player:get_player_name()] then - -- HACK for keeping backwards compatibility - minetest.log("warning", api_function .. " called on offline player") - return - end - return original_function(player, ...) - end -end diff --git a/mods/player_api/init.lua b/mods/player_api/init.lua deleted file mode 100644 index f258aea7..00000000 --- a/mods/player_api/init.lua +++ /dev/null @@ -1,26 +0,0 @@ -dofile(minetest.get_modpath("player_api") .. "/api.lua") - --- Default player appearance -player_api.register_model("character.b3d", { - animation_speed = 30, - textures = {"character.png"}, - animations = { - -- Standard animations. - stand = {x = 0, y = 79}, - lay = {x = 162, y = 166, eye_height = 0.3, override_local = true, - collisionbox = {-0.6, 0.0, -0.6, 0.6, 0.3, 0.6}}, - walk = {x = 168, y = 187}, - mine = {x = 189, y = 198}, - walk_mine = {x = 200, y = 219}, - sit = {x = 81, y = 160, eye_height = 0.8, override_local = true, - collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.0, 0.3}} - }, - collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3}, - stepheight = 0.6, - eye_height = 1.47, -}) - --- Update appearance when the player joins -minetest.register_on_joinplayer(function(player) - player_api.set_model(player, "character.b3d") -end) diff --git a/mods/player_api/license.txt b/mods/player_api/license.txt deleted file mode 100644 index bdc43154..00000000 --- a/mods/player_api/license.txt +++ /dev/null @@ -1,60 +0,0 @@ -License of source code ----------------------- - -GNU Lesser General Public License, version 2.1 -Copyright (C) 2011 celeron55, Perttu Ahola -Copyright (C) 2011 Various Minetest developers and contributors - -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: -https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html - - -Licenses of media (textures, models and sounds) ------------------------------------------------ - -Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) -Copyright (C) 2011 celeron55, Perttu Ahola -Copyright (C) 2012 MirceaKitsune -Copyright (C) 2012 Jordach -Copyright (C) 2015 kilbith -Copyright (C) 2016 sofar -Copyright (C) 2016 xunto -Copyright (C) 2016 Rogier-5 -Copyright (C) 2017 TeTpaAka -Copyright (C) 2017 Desour -Copyright (C) 2018 stujones11 -Copyright (C) 2019 An0n3m0us - -You are free to: -Share — copy and redistribute the material in any medium or format. -Adapt — remix, transform, and build upon the material for any purpose, even commercially. -The licensor cannot revoke these freedoms as long as you follow the license terms. - -Under the following terms: - -Attribution — You must give appropriate credit, provide a link to the license, and -indicate if changes were made. You may do so in any reasonable manner, but not in any way -that suggests the licensor endorses you or your use. - -ShareAlike — If you remix, transform, or build upon the material, you must distribute -your contributions under the same license as the original. - -No additional restrictions — You may not apply legal terms or technological measures that -legally restrict others from doing anything the license permits. - -Notices: - -You do not have to comply with the license for elements of the material in the public -domain or where your use is permitted by an applicable exception or limitation. -No warranties are given. The license may not give you all of the permissions necessary -for your intended use. For example, other rights such as publicity, privacy, or moral -rights may limit how you use the material. - -For more details: -http://creativecommons.org/licenses/by-sa/3.0/ diff --git a/mods/player_api/mod.conf b/mods/player_api/mod.conf deleted file mode 100644 index bf62327b..00000000 --- a/mods/player_api/mod.conf +++ /dev/null @@ -1,2 +0,0 @@ -name = player_api -description = Minetest Game mod: Manages player visuals diff --git a/mods/player_api/models/character.b3d b/mods/player_api/models/character.b3d deleted file mode 100644 index 3e0827e4..00000000 Binary files a/mods/player_api/models/character.b3d and /dev/null differ diff --git a/mods/player_api/models/character.blend b/mods/player_api/models/character.blend deleted file mode 100644 index a32c3438..00000000 Binary files a/mods/player_api/models/character.blend and /dev/null differ diff --git a/mods/player_api/models/character.png b/mods/player_api/models/character.png deleted file mode 100644 index 05021781..00000000 Binary files a/mods/player_api/models/character.png and /dev/null differ diff --git a/mods/player_api/textures/player.png b/mods/player_api/textures/player.png deleted file mode 100644 index 6d61c434..00000000 Binary files a/mods/player_api/textures/player.png and /dev/null differ diff --git a/mods/player_api/textures/player_back.png b/mods/player_api/textures/player_back.png deleted file mode 100644 index 5e9ef054..00000000 Binary files a/mods/player_api/textures/player_back.png and /dev/null differ diff --git a/mods/sbz_armor/init.lua b/mods/sbz_armor/init.lua index 148ff5c4..fe745ab5 100644 --- a/mods/sbz_armor/init.lua +++ b/mods/sbz_armor/init.lua @@ -26,13 +26,13 @@ local armor = { -- antimatter weaponery: more powerful than matter, get your armor -- strange weaponery: either it kills you instantly, or it does no damage -- force: explosions or charges - armor_groups = { fall_damage_add_percent = -100, matter = 100, light = 100, antimatter = 100, strange = 100, }, + armor_groups = { fall_damage_add_percent = -100, matter = 100, light = 100, antimatter = 100, strange = 100 }, armor_group_descriptions = { - matter = "Matter Protection", - light = "Laser Protection", - antimatter = "Antimatter Protection", - strange = "Strange Protection", - } + matter = 'Matter Protection', + light = 'Laser Protection', + antimatter = 'Antimatter Protection', + strange = 'Strange Protection', + }, } local disable_setting_texture = false @@ -44,19 +44,19 @@ sbz_api.armor = armor ---@return table armor.get_armor_pieces = function(ref) local meta = ref:get_meta() - local armor_data = core.deserialize(meta:get_string("armor")) or {} + local armor_data = core.deserialize(meta:get_string 'armor') or {} return armor_data end -- does texture and armor group stuff armor.load_armor_pieces = function(ref, data) local armor_groups = table.copy(armor.armor_groups) - local texture_mod = "" + local texture_mod = '' for k, v in pairs(data) do local stack = ItemStack(v) local def = stack:get_definition() local groups - if type(def.armor_groups) == "function" then + if type(def.armor_groups) == 'function' then groups = def.armor_groups(ref, stack) else groups = def.armor_groups @@ -67,7 +67,7 @@ armor.load_armor_pieces = function(ref, data) end -- texture... - texture_mod = texture_mod .. "^" .. def.armor_texture + texture_mod = texture_mod .. '^' .. def.armor_texture else data[k] = nil -- empty stacks end @@ -78,52 +78,44 @@ armor.load_armor_pieces = function(ref, data) local texture_index = 1 if props.textures[3] then texture_index = 3 end local base_texture = props.textures[texture_index] - local concat_find = string.find(base_texture, "%^") - if concat_find and concat_find ~= -1 then - base_texture = string.sub(base_texture, 1, concat_find - 1) - end + local concat_find = string.find(base_texture, '%^') + if concat_find and concat_find ~= -1 then base_texture = string.sub(base_texture, 1, concat_find - 1) end props.textures[texture_index] = base_texture .. texture_mod - if not disable_setting_texture then - ref:set_properties(props) - end + if not disable_setting_texture then ref:set_properties(props) end ref:set_armor_groups(armor_groups) - armor.pieces_to_inventory(data, core.get_inventory { type = "detached", name = "sbz_armor:" .. name }) + armor.pieces_to_inventory(data, core.get_inventory { type = 'detached', name = 'sbz_armor:' .. name }) -- nice trick directly copied from https://github.com/minetest-mods/3d_armor/blob/c224a73df74ae8559507421ee50e82bc1f85b61f/3d_armor_ui/init.lua#L17C1-L20C5 - if unified_inventory.current_page[name] == "armor" then - unified_inventory.set_inventory_formspec(ref, "armor") - end + if unified_inventory.current_page[name] == 'armor' then unified_inventory.set_inventory_formspec(ref, 'armor') end end armor.pieces_to_inventory = function(data, inv) - local mainlist = inv:get_list("main") + local mainlist = inv:get_list 'main' for id, piece_def in pairs(piece_types.by_id) do if not data[piece_def.name] then - mainlist[id] = ItemStack("") + mainlist[id] = ItemStack '' else mainlist[id] = ItemStack(data[piece_def.name]) end end - inv:set_list("main", mainlist) + inv:set_list('main', mainlist) end armor.pieces_from_inventory = function(ref, inv) local data = {} - local mainlist = inv:get_list("main") + local mainlist = inv:get_list 'main' for _, piece_stack in pairs(mainlist) do local def = piece_stack:get_definition() - if def.armor_type then - data[def.armor_type] = piece_stack:to_string() - end + if def.armor_type then data[def.armor_type] = piece_stack:to_string() end end armor.set_armor_pieces(ref, data) end armor.set_armor_pieces = function(ref, armor_data) local meta = ref:get_meta() - meta:set_string("armor", core.serialize(armor_data)) + meta:set_string('armor', core.serialize(armor_data)) armor.load_armor_pieces(ref, armor_data) end @@ -133,19 +125,13 @@ armor.register = function(name, def) local data = armor.get_armor_pieces(user) if data[def.armor_type] == nil then data[def.armor_type] = stack:take_item(1):to_string() - if def.on_equip then - def.on_equip(stack, user) - end + if def.on_equip then def.on_equip(stack, user) end else local data_at_that = data[def.armor_type] data[def.armor_type] = stack:take_item(1):to_string() stack = ItemStack(data_at_that) - if stack:get_definition().on_unequip then - stack:get_definition().on_unequip(user, stack) - end - if def.on_equip then - def.on_equip(ItemStack(data[def.armor_type]), user) - end + if stack:get_definition().on_unequip then stack:get_definition().on_unequip(user, stack) end + if def.on_equip then def.on_equip(ItemStack(data[def.armor_type]), user) end end armor.set_armor_pieces(user, data) return stack @@ -160,68 +146,59 @@ armor.register_piecetype = function(def) end core.register_on_joinplayer(function(player) - local inv = core.create_detached_inventory("sbz_armor:" .. player:get_player_name(), { - allow_move = function(inv, from_list, from_index, to_list, to_index, count, callback_player) - if callback_player:get_player_name() ~= player:get_player_name() then - return 0 + local inv = core.create_detached_inventory('sbz_armor:' .. player:get_player_name(), { + allow_move = function(inv, from_list, from_index, to_list, to_index, count, callback_player) + if callback_player:get_player_name() ~= player:get_player_name() then + return 0 + else + if callback_player:get_inventory():get_stack(from_index, from_index):get_definition().armor_type then + return count else - if callback_player:get_inventory():get_stack(from_index, from_index):get_definition().armor_type then - return count - else - return 0 - end - end - end, - allow_put = function(inv, listname, index, stack, callback_player) - if callback_player:get_player_name() ~= player:get_player_name() then - return 0 - end - local trigger_method = "on_equip" - local stack_def = stack:get_definition() - if not stack_def.armor_type then return 0 end -- thats not armor - - local data = armor.get_armor_pieces(player) - if data[stack_def.armor_type] or not ItemStack(data[stack_def.armor_type]):is_empty() then return 0 end - - if stack_def[trigger_method] then - stack_def[trigger_method](player, stack) - end - return stack:get_count() - end, - allow_take = function(inv, listname, index, stack, callback_player) - if callback_player:get_player_name() ~= player:get_player_name() then return 0 end - local trigger_method = "on_unequip" - local stack_def = stack:get_definition() - if not stack_def.armor_type then return 0 end -- thats not armor - - --local data = armor.get_armor_pieces(player) - --if data[stack_def.armor_type] or ItemStack(data[stack_def.armor_type]):is_empty() then return 0 end - - if stack_def[trigger_method] then - stack_def[trigger_method](player, stack) - end - return stack:get_count() - end, - - on_move = function(inv, from_list, from_index, to_list, to_index, count, player) - armor.pieces_from_inventory(player, inv) - end, - on_put = function(inv, listname, index, stack, player) - armor.pieces_from_inventory(player, inv) - end, - on_take = function(inv, listname, index, stack, player) - armor.pieces_from_inventory(player, inv) - end, - }, - player:get_player_name()) - inv:set_size("main", #piece_types.by_id) + end + end, + allow_put = function(inv, listname, index, stack, callback_player) + if callback_player:get_player_name() ~= player:get_player_name() then return 0 end + local trigger_method = 'on_equip' + local stack_def = stack:get_definition() + if not stack_def.armor_type then return 0 end -- thats not armor + + local data = armor.get_armor_pieces(player) + if data[stack_def.armor_type] or not ItemStack(data[stack_def.armor_type]):is_empty() then return 0 end + + if stack_def[trigger_method] then stack_def[trigger_method](player, stack) end + return stack:get_count() + end, + allow_take = function(inv, listname, index, stack, callback_player) + if callback_player:get_player_name() ~= player:get_player_name() then return 0 end + local trigger_method = 'on_unequip' + local stack_def = stack:get_definition() + if not stack_def.armor_type then return 0 end -- thats not armor + + --local data = armor.get_armor_pieces(player) + --if data[stack_def.armor_type] or ItemStack(data[stack_def.armor_type]):is_empty() then return 0 end + + if stack_def[trigger_method] then stack_def[trigger_method](player, stack) end + return stack:get_count() + end, + + on_move = function(inv, from_list, from_index, to_list, to_index, count, player) + armor.pieces_from_inventory(player, inv) + end, + on_put = function(inv, listname, index, stack, player) + armor.pieces_from_inventory(player, inv) + end, + on_take = function(inv, listname, index, stack, player) + armor.pieces_from_inventory(player, inv) + end, + }, player:get_player_name()) + inv:set_size('main', #piece_types.by_id) armor.load_armor_pieces(player, armor.get_armor_pieces(player)) end) core.register_on_leaveplayer(function(player) - core.remove_detached_inventory("sbz_armor:" .. player:get_player_name()) + core.remove_detached_inventory('sbz_armor:' .. player:get_player_name()) end) local max_wear = 65535 @@ -233,8 +210,8 @@ core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool local stack = ItemStack(v) local def = stack:get_definition() if def.on_punched then - no_damage = no_damage or - def.on_punched(stack, data, player, hitter, time_from_last_punch, tool_capabilities, dir, damage) + no_damage = no_damage + or def.on_punched(stack, data, player, hitter, time_from_last_punch, tool_capabilities, dir, damage) data[k] = stack:to_string() end end @@ -243,14 +220,13 @@ core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool for k, v in pairs(data) do local stack = ItemStack(v) local def = stack:get_definition() - if not def.custom_wear then + if not def.custom_wear and def.durability ~= nil then stack:set_wear(stack:get_wear() + ((max_wear / def.durability) * damage)) data[k] = stack:to_string() end end -- re-evaluate the armor groups - local armor_groups = table.copy(armor.armor_groups) for k, v in pairs(data) do @@ -258,7 +234,7 @@ core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool local def = stack:get_definition() if def.armor_groups then local groups - if type(def.armor_groups) == "function" then + if type(def.armor_groups) == 'function' then groups = def.armor_groups(player, stack) else groups = def.armor_groups @@ -274,56 +250,61 @@ core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool player:set_armor_groups(armor_groups) end) - -- piece types -armor.register_piecetype({ -- helmet - name = "head", -}) +armor.register_piecetype { -- helmet + name = 'head', +} -armor.register_piecetype({ -- chestplate - name = "torso", -}) +armor.register_piecetype { -- chestplate + name = 'torso', +} -armor.register_piecetype({ -- leggings - name = "legs", -}) -armor.register_piecetype({ -- boots - name = "feet", -}) +armor.register_piecetype { -- leggings + name = 'legs', +} +armor.register_piecetype { -- boots + name = 'feet', +} -- NOW the inventory! -unified_inventory.register_button("armor", { - type = "image", - image = "armor_icon.png", - tooltip = "Armor", +unified_inventory.register_button('armor', { + type = 'image', + image = 'armor_icon.png', + tooltip = 'Armor', }) -- todo: bars for what is protected against, and -unified_inventory.register_page("armor", { +unified_inventory.register_page('armor', { get_formspec = function(player) local hyper_text = {} -- hypertext text, got it? local player_armor = player:get_armor_groups() for group, desc in pairs(armor.armor_group_descriptions) do local protection = (1 - ((player_armor[group] or 0) / armor.armor_groups[group])) * 100 - hyper_text[#hyper_text + 1] = ("%s%% %s"):format(protection, desc) -- HEY THERE!!!! SOMEONE!!! if you don't understand what the hell that format string was, please learn how to use string.format, it's important!!! Its not that difficult like regex + hyper_text[#hyper_text + 1] = ('%s%% %s'):format(protection, desc) -- HEY THERE!!!! SOMEONE!!! if you don't understand what the hell that format string was, please learn how to use string.format, it's important!!! Its not that difficult like regex end local name = player:get_player_name() local props = player:get_properties() return { - formspec = string.format([[ + formspec = string.format( + [[ list[detached:sbz_armor:%s;main;0.2,0.2;2,10;] listring[detached:sbz_armor:%s;main] listring[current_player;main] model[3.3,0.2;3,5;model;%s;%s;0,180] hypertext[6.5,0.2;3,5;htext;%s] - ]], name, name, props.mesh, table.concat(props.textures, ","), - core.formspec_escape(table.concat(hyper_text, "\n"))), + ]], + name, + name, + props.mesh, + table.concat(props.textures, ','), + core.formspec_escape(table.concat(hyper_text, '\n')) + ), } end, }) -local modpath = core.get_modpath("sbz_armor") -dofile(modpath .. "/armor_recipes.lua") -dofile(modpath .. "/armor_types.lua") +local modpath = core.get_modpath 'sbz_armor' +dofile(modpath .. '/armor_recipes.lua') +dofile(modpath .. '/armor_types.lua') diff --git a/mods/sbz_base/init.lua b/mods/sbz_base/init.lua index 6b2cd706..0d858481 100644 --- a/mods/sbz_base/init.lua +++ b/mods/sbz_base/init.lua @@ -1,7 +1,7 @@ ---@diagnostic disable-next-line: lowercase-global sbz_api = { version = 40, - is_version_dev = false, + is_version_dev = true, gravity = 9.8 / 2, server_optimizations = (core.settings:get 'sbz_server_mode' or 'auto'), deg2rad = math.pi / 180, @@ -208,16 +208,21 @@ local function playRandomBGM(player) if player:get_meta() == nil then return end local player_name = player:get_player_name() + local player_meta = player:get_meta() + local random_index = math.random(1, #bgm_sounds) local sound_name = bgm_sounds[random_index] local sound_length = bgm_lengths[random_index] if handles[player_name] then core.sound_stop(handles[player_name]) end - local volume = player:get_meta():get_int 'volume' / 100 - if volume == 0 and player:get_meta():get_int 'has_set_volume' == 0 then volume = 1 end + + local volume = player_meta:get_int 'bgm_volume' / 100 + if volume == 0 and player_meta:get_int 'has_set_volume' == 0 then volume = 1 end + handles[player_name] = core.sound_play(sound_name, { to_player = player_name, gain = volume, }) + core.after( sound_length + math.random(10, 100), function() -- i introduce one second of complete silence here, just because -- yeah well I introduce three hundred -- yeah well guess what its random now @@ -239,7 +244,7 @@ core.register_chatcommand('bgm_volume', { local player = core.get_player_by_name(name or '') if not player then return end local meta = player:get_meta() - meta:set_int('volume', volume) + meta:set_int('bgm_volume', volume) meta:set_int('has_set_volume', 1) local handle = sbz_api.bgm_handles[player:get_player_name()] if handle then core.sound_fade(handle, 4, (volume / 100) + 0.001) end -- HACK: +0.001 so it doesn't delete the sound xDD @@ -254,12 +259,8 @@ core.register_on_joinplayer(function(player) player:get_player_name(), '‼ reminder: If you fall off, use /core to teleport back to the core.' ) - core.chat_send_player( - player:get_player_name(), - '‼ reminder: If lose your Quest Book, use /qb to get it back.' - ) - -- core.chat_send_player(player:get_player_name(), - -- "‼ Also, you can hold right click on the core, instead of having to spam your mouse, on mobile you might need to just hold tap") + core.chat_send_player(player:get_player_name(), '‼ reminder: If lose your Quest Book, use /qb to get it back.') + -- play bgm playRandomBGM(player) @@ -364,10 +365,12 @@ core.register_globalstep(function(_) end) -- inter-mod utils +-- use sbz_api.count_nodes_within_radius this is deprecated function count_nodes_within_radius(pos, nodenames, radius) local radius_vector = vector.new(radius, radius, radius) return #core.find_nodes_in_area(vector.subtract(pos, radius_vector), vector.add(pos, radius_vector), nodenames) end +sbz_api.count_nodes_within_radius = count_nodes_within_radius -- returns the first node pos function is_node_within_radius(pos, nodenames, radius) @@ -393,8 +396,11 @@ function sbz_api.on_place_precedence(on_place) if under then local node = core.get_node(under) local def = core.registered_nodes[node.name] - if def and def.on_rightclick and - not (placer and placer:is_player() and placer:get_player_control().sneak) then + if + def + and def.on_rightclick + and not (placer and placer:is_player() and placer:get_player_control().sneak) + then return def.on_rightclick(under, node, placer, itemstack, pointed_thing) or itemstack end end @@ -429,10 +435,9 @@ end https://github.com/mt-mods/technic/blob/379bedc20d7ab11c758afa52d5916b23dced5354/technic/helpers.lua#L102 to line 107 ]] -local get_or_load_node_node function sbz_api.get_or_load_node(pos) - get_or_load_node_node = core.get_node_or_nil(pos) - if get_or_load_node_node then return get_or_load_node_node end + local node = core.get_node_or_nil(pos) + if node then return node end core.load_area(pos) return core.get_node(pos) end @@ -458,6 +463,7 @@ dofile(MP .. '/recipe.lua') dofile(MP .. '/serialize.lua') dofile(MP .. '/serialize_benchmark.lua') dofile(MP .. '/space_movement.lua') +dofile(MP .. '/pick_block.lua') -- yeah you actually have to do this -- definition copied from mtg diff --git a/mods/sbz_base/pick_block.lua b/mods/sbz_base/pick_block.lua new file mode 100644 index 00000000..9a65e20d --- /dev/null +++ b/mods/sbz_base/pick_block.lua @@ -0,0 +1,52 @@ +-- Common problem: "I want to have this node in my hand right now" +-- Alternative: Hotbar switching (implemented) +-- +-- Keys: zoom+rmb/lmb to pick + +local function get_pointed_node_type(player) + local lookdir = player:get_look_dir() + local eyepos = sbz_api.get_pos_with_eye_height(player) + local ray = core.raycast(eyepos, vector.add(eyepos, vector.multiply(lookdir, 30)), false, false) + for pointed_thing in ray do + if pointed_thing.type == 'node' then + local node = core.get_node(pointed_thing.under) + local def = core.registered_nodes[node.name] + if def then return node.name end + end + end + return nil +end + +local function pick_block(player, node_name) + local inv = player:get_inventory() + if not inv:contains_item('main', node_name) then return end + + local item_index + + local list = inv:get_list 'main' + for i = 1, #list do + local stack = list[i] + if stack:get_name() == node_name then + item_index = i + break + end + end + + if not item_index then return end + local wielded_item = player:get_wielded_item() + local wield_index = player:get_wield_index() + list[wield_index] = list[item_index] + list[item_index] = wielded_item + inv:set_list('main', list) + core.chat_send_player( + player:get_player_name(), + '[sbz] Picked ' .. node_name .. ' to wielded slot. (Use zoom+rmb/lmb to pick a node.)' + ) +end + +controls.register_on_press(function(player, key) + if not (key == 'LMB' or key == 'RMB') then return end + if not player:get_player_control().zoom then return end + local node_name = get_pointed_node_type(player) + if node_name then pick_block(player, node_name) end +end) diff --git a/mods/sbz_base/space_movement.lua b/mods/sbz_base/space_movement.lua index 7870969b..f82dcf68 100644 --- a/mods/sbz_base/space_movement.lua +++ b/mods/sbz_base/space_movement.lua @@ -5,9 +5,11 @@ local function space_movement_step() for _, player in pairs(core.get_connected_players()) do local controls = player:get_player_control() if controls.jump and controls.aux1 then - player_monoids.gravity:add_change(player, 0, 'sbz_api:???') + if not sbz_api.jetpack_users[player:get_player_name()] then + player_monoids.gravity:add_change(player, 0, 'sbz_api:space_movement') + end else - player_monoids.gravity:del_change(player, 'sbz_api:???') + player_monoids.gravity:del_change(player, 'sbz_api:space_movement') end end end diff --git a/mods/sbz_bio/plants.lua b/mods/sbz_bio/plants.lua index f1347bcc..33ffbe8c 100644 --- a/mods/sbz_bio/plants.lua +++ b/mods/sbz_bio/plants.lua @@ -11,56 +11,57 @@ function sbz_api.plant_grow(next_stage) end local warpshroom_family = { - "sbz_bio:warpshroom", - "sbz_bio:shockshroom", - "sbz_bio:stemfruit_plant" + 'sbz_bio:warpshroom', + 'sbz_bio:shockshroom', + 'sbz_bio:stemfruit_plant', } local pyrograss_family = { - "sbz_bio:pyrograss", - "sbz_bio:razorgrass", - "sbz_bio:cleargrass", - "sbz_bio:stemfruit_plant", + 'sbz_bio:pyrograss', + 'sbz_bio:razorgrass', + 'sbz_bio:cleargrass', + 'sbz_bio:stemfruit_plant', } local fiberweed_family = { - "sbz_bio:fiberweed", + 'sbz_bio:fiberweed', } local can_turn_into = { - ["sbz_bio:stemfruit_plant"] = { - "sbz_bio:pyrograss", - "sbz_bio:razorgrass", - "sbz_bio:cleargrass", - "sbz_bio:warpshroom", - "sbz_bio:shockshroom", + ['sbz_bio:stemfruit_plant'] = { + 'sbz_bio:pyrograss', + 'sbz_bio:razorgrass', + 'sbz_bio:cleargrass', + 'sbz_bio:warpshroom', + 'sbz_bio:shockshroom', -- "sbz_bio:fiberweed", }, - ["sbz_bio:warpshroom"] = warpshroom_family, - ["sbz_bio:shockshroom"] = warpshroom_family, - ["sbz_bio:pyrograss"] = pyrograss_family, - ["sbz_bio:razorgrass"] = pyrograss_family, - ["sbz_bio:cleargrass"] = pyrograss_family, - ["sbz_bio:fiberweed"] = fiberweed_family, + ['sbz_bio:warpshroom'] = warpshroom_family, + ['sbz_bio:shockshroom'] = warpshroom_family, + ['sbz_bio:pyrograss'] = pyrograss_family, + ['sbz_bio:razorgrass'] = pyrograss_family, + ['sbz_bio:cleargrass'] = pyrograss_family, + ['sbz_bio:fiberweed'] = fiberweed_family, } -local special_cases = { ["sbz_bio:fiberweed"] = true } +local special_cases = { ['sbz_bio:fiberweed'] = true } + +-- i actually forgot what this is for +-- i assume it's for checking if the stuff above is good core.after(0, function() - -- dev script for checking if the stuff i write is actually valid for initial_node, node_list in pairs(can_turn_into) do - if not core.registered_nodes[initial_node .. "_1"] and not special_cases[initial_node] then - error("Uh oh: " .. - initial_node) + if not core.registered_nodes[initial_node .. '_1'] and not special_cases[initial_node] then + error('Uh oh: ' .. initial_node) end for k, v in pairs(node_list) do - if not core.registered_nodes[v .. "_1"] and not special_cases[v] then error("Uh oh:" .. v) end + if not core.registered_nodes[v .. '_1'] and not special_cases[v] then error('Uh oh:' .. v) end end end end) local radiation_check = function(pos) - local nodes = core.find_nodes_in_area(pos - vector.new(5, 5, 5), pos + vector.new(5, 5, 5), - { "group:radioactive" }, true) + local nodes = + core.find_nodes_in_area(pos - vector.new(5, 5, 5), pos + vector.new(5, 5, 5), { 'group:radioactive' }, true) local rad = 0 for nodename, poslist in pairs(nodes) do - local val = core.get_item_group(nodename, "radioactive") + local val = core.get_item_group(nodename, 'radioactive') for _, radpos in ipairs(poslist) do local dist = vector.distance(pos, radpos) rad = rad + ((val ^ 2) / math.max(0.75, dist ^ 2)) @@ -69,8 +70,6 @@ local radiation_check = function(pos) return rad -- so rad end - - function sbz_api.plant_growth_tick(num_ticks, mutation_chance) return function(pos, node) local rad = radiation_check(pos) @@ -86,10 +85,10 @@ function sbz_api.plant_growth_tick(num_ticks, mutation_chance) local newnode = table.copy(node) local nodepos = vector.copy(pos) if special_cases[should_turn_into] then - if should_turn_into == "sbz_bio:fiberweed" then - if core.get_node(vector.subtract(nodepos, vector.new(0, 1, 0))).name == "sbz_bio:dirt" then + if should_turn_into == 'sbz_bio:fiberweed' then + if core.get_node(vector.subtract(nodepos, vector.new(0, 1, 0))).name == 'sbz_bio:dirt' then nodepos = vector.subtract(nodepos, vector.new(0, 1, 0)) - should_turn_into = "sbz_bio:fiberweed" + should_turn_into = 'sbz_bio:fiberweed' else return true -- can't mutate end @@ -103,30 +102,30 @@ function sbz_api.plant_growth_tick(num_ticks, mutation_chance) end if sbz_api.get_node_heat(pos) > 7 and sbz_api.is_hydrated(pos) then local meta = minetest.get_meta(pos) - local count = meta:get_int("count") + 1 + local count = meta:get_int 'count' + 1 local under = vector.copy(pos) under.y = under.y - 1 local growth_multiplier = 1 - local soil = core.get_item_group((sbz_api.get_or_load_node(under) or { name = "" }).name, "soil") + local soil = core.get_item_group((sbz_api.get_or_load_node(under) or { name = '' }).name, 'soil') growth_multiplier = math.max(0, growth_multiplier + (soil - 1)) iterate_around_pos(pos, function(ipos) local n = sbz_api.get_or_load_node(ipos) - if n and core.get_item_group(n.name, "growth_boost") > 0 then - growth_multiplier = growth_multiplier + - (growth_multiplier * (core.get_item_group(n.name, "growth_boost") / 100)) + if n and core.get_item_group(n.name, 'growth_boost') > 0 then + growth_multiplier = growth_multiplier + + (growth_multiplier * (core.get_item_group(n.name, 'growth_boost') / 100)) end end) - meta:set_float("growth_multiplier", growth_multiplier) + meta:set_float('growth_multiplier', growth_multiplier) if count >= (num_ticks / growth_multiplier) then count = 0 minetest.registered_nodes[node.name].grow(pos, node) end - meta:set_int("count", count) + meta:set_int('count', count) return true end end @@ -134,9 +133,9 @@ end function sbz_api.plant_wilt(stages) return function(pos, node) - if core.get_item_group(node.name, "no_wilt") == 0 then + if core.get_item_group(node.name, 'no_wilt') == 0 then node.param2 = node.param2 + 1 - minetest.swap_node(pos, node.param2 >= stages and { name = "air" } or node) + minetest.swap_node(pos, node.param2 >= stages and { name = 'air' } or node) end end end @@ -144,13 +143,14 @@ end function sbz_api.plant_plant(plant, nodes) return sbz_api.on_place_precedence(function(itemstack, user, pointed) for _, node in ipairs(nodes) do - local use_pointed = "above" - if pointed.switched then - use_pointed = "under" - end + local use_pointed = 'above' + if pointed.switched then use_pointed = 'under' end local soil_node = core.get_node(pointed[use_pointed] - up) - if string.sub(node, 1, 6) == "group:" and minetest.get_item_group(soil_node.name, string.sub(node, 7)) > 0 - or soil_node.name == node then + if + string.sub(node, 1, 6) == 'group:' + and minetest.get_item_group(soil_node.name, string.sub(node, 7)) > 0 + or soil_node.name == node + then local _, pos = minetest.item_place_node(ItemStack(plant), user, pointed) if pos then itemstack:take_item() @@ -163,7 +163,7 @@ function sbz_api.plant_plant(plant, nodes) end function sbz_api.register_plant(name, defs) - defs.description = defs.description or "" + defs.description = defs.description or '' defs.drop = defs.drop defs.growth_rate = defs.growth_rate or 1 defs.co2_demand = defs.co2_demand or 0 @@ -178,16 +178,19 @@ function sbz_api.register_plant(name, defs) for i = 1, defs.stages - 1 do local interpolant = (i - 1) / (defs.stages - 1) local height = defs.height_min * (1 - interpolant) + defs.height_max * interpolant - minetest.register_node("sbz_bio:" .. name .. "_" .. i, { + minetest.register_node('sbz_bio:' .. name .. '_' .. i, { description = defs.description, - drawtype = "plantlike", - tiles = { name .. "_" .. i .. ".png" }, - inventory_image = name .. "_" .. i .. ".png", - selection_box = { type = "fixed", fixed = { -defs.width, -0.5, -defs.width, defs.width, height, defs.width } }, - paramtype = "light", + drawtype = 'plantlike', + tiles = { name .. '_' .. i .. '.png' }, + inventory_image = name .. '_' .. i .. '.png', + selection_box = { + type = 'fixed', + fixed = { -defs.width, -0.5, -defs.width, defs.width, height, defs.width }, + }, + paramtype = 'light', sunlight_propagates = true, - paramtype2 = "color", - palette = "wilting_palette.png", + paramtype2 = 'color', + palette = 'wilting_palette.png', walkable = false, groups = { dig_immediate = 2, @@ -200,26 +203,29 @@ function sbz_api.register_plant(name, defs) burn = 1, nb_nodig = 1, no_wilt = defs.no_wilt and 1 or 0, - growth_boost = growth_boost_base * i + growth_boost = growth_boost_base * i, }, drop = {}, growth_tick = sbz_api.plant_growth_tick(defs.growth_rate, defs.mutation_chance or 10), - grow = sbz_api.plant_grow("sbz_bio:" .. name .. "_" .. (i + 1)), + grow = sbz_api.plant_grow('sbz_bio:' .. name .. '_' .. (i + 1)), wilt = sbz_api.plant_wilt(2), sbz_player_inside = defs.sbz_player_inside, - power_per_co2 = power_per_co2_base * i + power_per_co2 = power_per_co2_base * i, }) end - minetest.register_node("sbz_bio:" .. name .. "_" .. defs.stages, { + minetest.register_node('sbz_bio:' .. name .. '_' .. defs.stages, { description = defs.description, - drawtype = "plantlike", - tiles = { name .. "_" .. defs.stages .. ".png" }, - inventory_image = name .. "_" .. defs.stages .. ".png", - selection_box = { type = "fixed", fixed = { -defs.width, -0.5, -defs.width, defs.width, defs.height_max, defs.width } }, - paramtype = "light", + drawtype = 'plantlike', + tiles = { name .. '_' .. defs.stages .. '.png' }, + inventory_image = name .. '_' .. defs.stages .. '.png', + selection_box = { + type = 'fixed', + fixed = { -defs.width, -0.5, -defs.width, defs.width, defs.height_max, defs.width }, + }, + paramtype = 'light', sunlight_propagates = true, - paramtype2 = "color", - palette = "wilting_palette.png", + paramtype2 = 'color', + palette = 'wilting_palette.png', walkable = false, groups = { oddly_breakable_by_hand = 3, @@ -231,35 +237,39 @@ function sbz_api.register_plant(name, defs) not_in_creative_inventory = 1, burn = 1, needs_co2 = defs.use_co2_in_final_stage and defs.co2_demand or 0, - growth_boost = defs.growth_boost + growth_boost = defs.growth_boost, }, drop = defs.drop, sbz_player_inside = defs.sbz_player_inside, power_per_co2 = defs.power_per_co2, - growth_tick = defs.use_co2_in_final_stage and function() return true end, - grow = defs.use_co2_in_final_stage and function() return true end, - wilt = defs.use_co2_in_final_stage and sbz_api.plant_wilt(2) + growth_tick = defs.use_co2_in_final_stage and function() + return true + end, + grow = defs.use_co2_in_final_stage and function() + return true + end, + wilt = defs.use_co2_in_final_stage and sbz_api.plant_wilt(2), }) end -- PYROGRASS FAMILY --Pyrograss, hardy and quick to grow, highly flammable due to its carbon content --To be used in rockets and explosives and stuff -sbz_api.register_plant("pyrograss", { - description = "Pyrograss Plant", - drop = "sbz_bio:pyrograss 2", +sbz_api.register_plant('pyrograss', { + description = 'Pyrograss Plant', + drop = 'sbz_bio:pyrograss 2', growth_rate = 4, - family = "sbz_bio:pyrograss", + family = 'sbz_bio:pyrograss', width = 0.25, height_min = -0.375, height_max = 0, no_wilt = true, }) -minetest.register_craftitem("sbz_bio:pyrograss", { - description = "Pyrograss", - inventory_image = "pyrograss_4.png", +minetest.register_craftitem('sbz_bio:pyrograss', { + description = 'Pyrograss', + inventory_image = 'pyrograss_4.png', groups = { burn = 30, eat = 1 }, - on_place = sbz_api.plant_plant("sbz_bio:pyrograss_1", { "group:soil" }) + on_place = sbz_api.plant_plant('sbz_bio:pyrograss_1', { 'group:soil' }), }) -- uses: @@ -270,48 +280,59 @@ minetest.register_craftitem("sbz_bio:pyrograss", { -- grows slightly slower than pyro -- no co2 demand - -playereffects.register_effect_type("poison", "Poisoned", "fx_poison.png", - { "clearable", "negative", "speed", "health", "negative_health" }, +playereffects.register_effect_type( + 'poison', + 'Poisoned', + 'fx_poison.png', + { 'clearable', 'negative', 'speed', 'health', 'negative_health' }, function(player) - unlock_achievement(player:get_player_name(), "Poisoned") + unlock_achievement(player:get_player_name(), 'Poisoned') player:set_hp(math.max(0, player:get_hp() - 1)) - player_monoids.speed:add_change(player, 0.5, "sbz_bio:poison") - end, function(fx, player) - player_monoids.speed:del_change(player, "sbz_bio:poison") - end, false, true, 2) - -sbz_api.register_plant("razorgrass", { - description = "Razorgrass Plant", - drop = "sbz_bio:razorgrass 2", + player_monoids.speed:add_change(player, 0.5, 'sbz_bio:poison') + end, + function(fx, player) + player_monoids.speed:del_change(player, 'sbz_bio:poison') + end, + false, + true, + 2 +) + +sbz_api.register_plant('razorgrass', { + description = 'Razorgrass Plant', + drop = 'sbz_bio:razorgrass 2', growth_rate = 8, - family = "pyrograss", + family = 'pyrograss', width = 0.25, height_min = -0.375, height_max = 0, sbz_player_inside = function(pos, player) - playereffects.apply_effect_type("poison", 2, player) - end + if not player:is_valid() then return end + player:set_hp(player:get_hp() - 1) + playereffects.apply_effect_type('poison', 2, player) + end, }) core.register_craft { - type = "shapeless", - output = "sbz_bio:fertilizer 20", + type = 'shapeless', + output = 'sbz_bio:fertilizer 20', recipe = { - "sbz_bio:razorgrass", "sbz_bio:razorgrass", "sbz_bio:razorgrass" - } + 'sbz_bio:razorgrass', + 'sbz_bio:razorgrass', + 'sbz_bio:razorgrass', + }, } -minetest.register_craftitem("sbz_bio:razorgrass", { - description = "Razorgrass", - inventory_image = "razorgrass_4.png", +minetest.register_craftitem('sbz_bio:razorgrass', { + description = 'Razorgrass', + inventory_image = 'razorgrass_4.png', groups = { burn = 2, eat = -8 }, - eat_fx = { "Poisoned", "Slowed" }, - on_place = sbz_api.plant_plant("sbz_bio:razorgrass_1", { "group:soil" }), + eat_fx = { 'Poisoned', 'Slowed' }, + on_place = sbz_api.plant_plant('sbz_bio:razorgrass_1', { 'group:soil' }), on_use = function(stack, user, pointed) if user.is_fake_player then return end - playereffects.apply_effect_type("poison", 10, user) + playereffects.apply_effect_type('poison', 10, user) return core.item_eat(-8)(stack, user, pointed) - end + end, }) -- cleargass, uses: @@ -321,49 +342,46 @@ minetest.register_craftitem("sbz_bio:razorgrass", { -- boosts plants near it by 15% when in full growth stage, consumes co2, makes plants consume more co2 -- grows slowly -playereffects.register_effect_type("immune", "Immune", "fx_immunity.png", { "immunity" }, - function(player) - unlock_achievement(player:get_player_name(), "Immune") - playereffects.cancel_effect_group("clearable", player:get_player_name()) - end, function(fx, player) end, false, true, 0.1) +playereffects.register_effect_type('immune', 'Immune', 'fx_immunity.png', { 'immunity' }, function(player) + unlock_achievement(player:get_player_name(), 'Immune') + playereffects.cancel_effect_group('clearable', player:get_player_name()) +end, function(fx, player) end, false, true, 0.1) -sbz_api.register_plant("cleargrass", { - description = "Cleargrass Plant", - drop = "sbz_bio:cleargrass 2", +sbz_api.register_plant('cleargrass', { + description = 'Cleargrass Plant', + drop = 'sbz_bio:cleargrass 2', growth_rate = 4, co2_demand = 5, - family = "pyrograss", + family = 'pyrograss', width = 0.25, height_min = -0.375, height_max = 0, sbz_player_inside = function(pos, player) - playereffects.apply_effect_type("immune", 10, player) + playereffects.apply_effect_type('immune', 10, player) end, use_co2_in_final_stage = true, growth_boost = 25, - }) -minetest.register_craftitem("sbz_bio:cleargrass", { - description = "Cleargrass", - inventory_image = "cleargrass_4.png", +minetest.register_craftitem('sbz_bio:cleargrass', { + description = 'Cleargrass', + inventory_image = 'cleargrass_4.png', groups = { burn = 0, eat = 0 }, - eat_fx = { "Cleared" }, - on_place = sbz_api.plant_plant("sbz_bio:cleargrass_1", { "group:soil" }), + eat_fx = { 'Cleared' }, + on_place = sbz_api.plant_plant('sbz_bio:cleargrass_1', { 'group:soil' }), on_use = function(stack, user, pointed) - playereffects.apply_effect_type("immune", 30 / 0.1, user) + playereffects.apply_effect_type('immune', 30 / 0.1, user) return core.item_eat(0)(stack, user, pointed) - end + end, }) - -- STEMFRUIT: base of all plants --Stemfruit, generic plant, quite versatile --To be used to craft other plants -sbz_api.register_plant("stemfruit_plant", { - description = "Stemfruit Plant", - drop = "sbz_bio:stemfruit 3", - family = "stemfruit", +sbz_api.register_plant('stemfruit_plant', { + description = 'Stemfruit Plant', + drop = 'sbz_bio:stemfruit 3', + family = 'stemfruit', growth_rate = 8, co2_demand = 1, width = 0.125, @@ -372,40 +390,37 @@ sbz_api.register_plant("stemfruit_plant", { mutation_chance = 80, }) -minetest.register_craftitem("sbz_bio:stemfruit", { - description = "Stemfruit", - inventory_image = "stemfruit.png", +minetest.register_craftitem('sbz_bio:stemfruit', { + description = 'Stemfruit', + inventory_image = 'stemfruit.png', groups = { burn = 12, eat = 5 }, on_place = sbz_api.on_place_precedence(function(itemstack, user, pointed) - local use_pointed = "above" - if pointed.switched then - use_pointed = "under" - end + local use_pointed = 'above' + if pointed.switched then use_pointed = 'under' end local soil_node = core.get_node(pointed[use_pointed] - up) local water_node = core.get_node(pointed[use_pointed] + up) - if minetest.get_item_group(soil_node.name, "soil") > 0 then - if core.get_item_group(water_node.name, "water") > 0 then - return core.registered_items["sbz_bio:fiberweed"].on_place(itemstack, user, pointed) + if minetest.get_item_group(soil_node.name, 'soil') > 0 then + if core.get_item_group(water_node.name, 'water') > 0 then + return core.registered_items['sbz_bio:fiberweed'].on_place(itemstack, user, pointed) end - local _, pos = minetest.item_place_node(ItemStack("sbz_bio:stemfruit_plant_1"), user, pointed) + local _, pos = minetest.item_place_node(ItemStack 'sbz_bio:stemfruit_plant_1', user, pointed) if pos then itemstack:take_item() return itemstack end return end - end) --sbz_api.plant_plant("sbz_bio:stemfruit_plant_1", { "group:soil" }) + end), --sbz_api.plant_plant("sbz_bio:stemfruit_plant_1", { "group:soil" }) }) - -- SHROOM FAMILY --Warpshroom, grows slowly, has teleportation powers --To be used later in teleporters -sbz_api.register_plant("warpshroom", { - description = "Warpshroom Plant", - drop = "sbz_bio:warpshroom 2", - family = "warpshroom", +sbz_api.register_plant('warpshroom', { + description = 'Warpshroom Plant', + drop = 'sbz_bio:warpshroom 2', + family = 'warpshroom', growth_rate = 16, co2_demand = 1, width = 0.25, @@ -417,13 +432,17 @@ local warpshroom_teleport_radius = 16 local function teleport_randomly(user) local user_pos = vector.round(user:get_pos()) for _ = 1, 1000 do - local pos = user_pos + - vector.new(math.random(-warpshroom_teleport_radius, warpshroom_teleport_radius), + local pos = user_pos + + vector.new( math.random(-warpshroom_teleport_radius, warpshroom_teleport_radius), - math.random(-warpshroom_teleport_radius, warpshroom_teleport_radius)) - if not minetest.registered_nodes[minetest.get_node(pos).name].walkable + math.random(-warpshroom_teleport_radius, warpshroom_teleport_radius), + math.random(-warpshroom_teleport_radius, warpshroom_teleport_radius) + ) + if + not minetest.registered_nodes[minetest.get_node(pos).name].walkable and not minetest.registered_nodes[minetest.get_node(pos + up).name].walkable - and minetest.registered_nodes[minetest.get_node(pos - up).name].walkable then + and minetest.registered_nodes[minetest.get_node(pos - up).name].walkable + then return user:set_pos(pos - up * 0.5) -- this was missing a "return"... yeah... bad bad end end @@ -431,16 +450,16 @@ end local eat = core.item_eat(6) -minetest.register_craftitem("sbz_bio:warpshroom", { - description = "Warpshroom", - inventory_image = "warpshroom_4.png", - on_place = sbz_api.plant_plant("sbz_bio:warpshroom_1", { "group:matter" }), +minetest.register_craftitem('sbz_bio:warpshroom', { + description = 'Warpshroom', + inventory_image = 'warpshroom_4.png', + on_place = sbz_api.plant_plant('sbz_bio:warpshroom_1', { 'group:matter' }), on_use = function(itemstack, user, pointed) teleport_randomly(user) - unlock_achievement(user:get_player_name(), "Not Chorus Fruit") + unlock_achievement(user:get_player_name(), 'Not Chorus Fruit') return eat(itemstack, user, pointed) end, - groups = { ui_bio = 1, eat = 6 } + groups = { ui_bio = 1, eat = 6 }, }) --[[ minetest.register_craft({ @@ -451,7 +470,7 @@ minetest.register_craft({ ]] -- Shockshroom, +40 power, needs 2 co2 -- ingredient in powered dirt -playereffects.register_effect_type("shocked", "Shocked", "fx_shocked.png", { "clearable", "speed" }, function(player) +playereffects.register_effect_type('shocked', 'Shocked', 'fx_shocked.png', { 'clearable', 'speed' }, function(player) ---modifies the vector, returns it local abs_y = function(vec) vec.y = math.abs(vec.y) @@ -459,13 +478,13 @@ playereffects.register_effect_type("shocked", "Shocked", "fx_shocked.png", { "cl end -- mild loss of control and bad combo with wet - unlock_achievement(player:get_player_name(), "Shocked") + unlock_achievement(player:get_player_name(), 'Shocked') player:add_velocity(vector.multiply(vector.random_direction(), 2)) - player_monoids.speed:add_change(player, 1.2, "sbz_bio:shocked") - if playereffects.has_effect_type(player:get_player_name(), "wet") then + player_monoids.speed:add_change(player, 1.2, 'sbz_bio:shocked') + if playereffects.has_effect_type(player:get_player_name(), 'wet') then player:set_hp(math.max(0, player:get_hp() - 8)) local pos = player:get_pos() - minetest.add_particlespawner({ + minetest.add_particlespawner { amount = 50, time = 0.1, radius = { min = 0.1, max = 0.5 }, @@ -475,93 +494,95 @@ playereffects.register_effect_type("shocked", "Shocked", "fx_shocked.png", { "cl size = { min = 0.5, max = 1 }, collisiondetection = false, vertical = false, - texture = "star.png^[colorize:yellow:255", - glow = 14 - }) + texture = 'star.png^[colorize:yellow:255', + glow = 14, + } player:add_velocity(abs_y(vector.multiply(vector.random_direction(), 8))) - playereffects.cancel_effect_type("shocked", false, player:get_player_name()) + playereffects.cancel_effect_type('shocked', false, player:get_player_name()) end end, function(fx, player) - player_monoids.speed:del_change(player, "sbz_bio:shocked") + player_monoids.speed:del_change(player, 'sbz_bio:shocked') end, false, true, 0.5) -sbz_api.register_plant("shockshroom", { - description = "Shockshroom Plant", - drop = "sbz_bio:shockshroom 2", - family = "warpshroom", +sbz_api.register_plant('shockshroom', { + description = 'Shockshroom Plant', + drop = 'sbz_bio:shockshroom 2', + family = 'warpshroom', growth_rate = 6, co2_demand = 2, width = 0.25, height_min = -0.3125, height_max = 0.25, sbz_player_inside = function(pos, player) - playereffects.apply_effect_type("shocked", 2 / 0.5, player) + playereffects.apply_effect_type('shocked', 2 / 0.5, player) end, power_per_co2 = 30, use_co2_in_final_stage = true, }) -minetest.register_craftitem("sbz_bio:shockshroom", { - description = "Shockshroom", - inventory_image = "shockshroom_4.png", - on_place = sbz_api.plant_plant("sbz_bio:shockshroom_1", { "group:soil" }), +minetest.register_craftitem('sbz_bio:shockshroom', { + description = 'Shockshroom', + inventory_image = 'shockshroom_4.png', + on_place = sbz_api.plant_plant('sbz_bio:shockshroom_1', { 'group:soil' }), groups = { ui_bio = 1, eat = -1 }, on_use = function(stack, user, pointed) if user.is_fake_player then return end - playereffects.apply_effect_type("shocked", 180 / 0.5, user, 0.5) + playereffects.apply_effect_type('shocked', 180 / 0.5, user, 0.5) return core.item_eat(-1)(stack, user, pointed) - end + end, }) -- STARFRUIT FAMILY (unlike the others, there is no base "starfruit" plant) -- todo - --Fiberweed, multi-node seaweed which grows vertically, requires water along its entire stem --To be used in making string and fabric for various uses local function is_all_water(pos, leveled) for i = 1, math.ceil(leveled / 16) do local nodename = minetest.get_node(pos + up * i).name - if nodename ~= "sbz_resources:water_source" and nodename ~= "sbz_resources:water_flowing" then return false end + if nodename ~= 'sbz_resources:water_source' and nodename ~= 'sbz_resources:water_flowing' then return false end end return true end -minetest.register_node("sbz_bio:fiberweed", { - description = "Fiberweed", - drawtype = "plantlike_rooted", - tiles = { "dirt.png^fiberweed_overlay.png", "dirt.png" }, - special_tiles = { { name = "fiberweed_plant.png", tileable_vertical = true } }, - inventory_image = "fiberweed_plant.png", - selection_box = { type = "fixed", fixed = { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }, { -0.25, 0.5, -0.25, 0.25, 1.25, 0.25 } } }, - paramtype2 = "leveled", +minetest.register_node('sbz_bio:fiberweed', { + description = 'Fiberweed', + drawtype = 'plantlike_rooted', + tiles = { 'dirt.png^fiberweed_overlay.png', 'dirt.png' }, + special_tiles = { { name = 'fiberweed_plant.png', tileable_vertical = true } }, + inventory_image = 'fiberweed_plant.png', + selection_box = { + type = 'fixed', + fixed = { { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }, { -0.25, 0.5, -0.25, 0.25, 1.25, 0.25 } }, + }, + paramtype2 = 'leveled', place_param2 = 8, groups = { matter = 1, plant = 1, needs_co2 = 1, transparent = 1, burn = 2 }, drop = {}, - node_dig_prediction = "sbz_bio:dirt", - node_placement_prediction = "", + node_dig_prediction = 'sbz_bio:dirt', + node_placement_prediction = '', on_place = function(itemstack, user, pointed) - if pointed.type ~= "node" or minetest.get_node(pointed.under).name ~= "sbz_bio:dirt" then return end - minetest.set_node(pointed.under, { name = "sbz_bio:fiberweed", param2 = 8 }) + if pointed.type ~= 'node' or minetest.get_node(pointed.under).name ~= 'sbz_bio:dirt' then return end + minetest.set_node(pointed.under, { name = 'sbz_bio:fiberweed', param2 = 8 }) itemstack:take_item() return itemstack end, after_dig_node = function(pos, node, meta, user) - minetest.set_node(pos, { name = "sbz_bio:dirt" }) - unlock_achievement(user:get_player_name(), "Fiberweed") + minetest.set_node(pos, { name = 'sbz_bio:dirt' }) + unlock_achievement(user:get_player_name(), 'Fiberweed') local inv = user:get_inventory() - local drop = inv:add_item("main", "sbz_bio:fiberweed " .. math.floor(node.param2 / 8)) + local drop = inv:add_item('main', 'sbz_bio:fiberweed ' .. math.floor(node.param2 / 8)) if drop then minetest.add_item(pos + up, drop) end end, growth_tick = function(pos, node) if sbz_api.get_node_heat(pos + up) > 7 and is_all_water(pos, node.param2) then local meta = minetest.get_meta(pos) - local count = meta:get_int("count") + 1 + local count = meta:get_int 'count' + 1 if count >= 4 then count = 0 minetest.registered_nodes[node.name].grow(pos, node) end - meta:set_int("count", count) + meta:set_int('count', count) return true end end, @@ -573,8 +594,8 @@ minetest.register_node("sbz_bio:fiberweed", { end, wilt = function(pos, node) node.param2 = node.param2 - 8 - minetest.swap_node(pos, node.param2 <= 0 and { name = "sbz_bio:dirt" } or node) - end + minetest.swap_node(pos, node.param2 <= 0 and { name = 'sbz_bio:dirt' } or node) + end, }) --[[ minetest.register_craft({ diff --git a/mods/sbz_decor/init.lua b/mods/sbz_decor/init.lua index d2dcf63d..97583511 100644 --- a/mods/sbz_decor/init.lua +++ b/mods/sbz_decor/init.lua @@ -1,150 +1,172 @@ -local modpath = minetest.get_modpath("sbz_decor") +local modpath = minetest.get_modpath 'sbz_decor' - -minetest.register_node("sbz_decor:photonlamp", { - description = "Photon Lamp", - drawtype = "mesh", - mesh = "photonlamp.obj", - tiles = { "photonlamp.png" }, +minetest.register_node('sbz_decor:photonlamp', { + description = 'Photon Lamp', + drawtype = 'mesh', + mesh = 'photonlamp.obj', + tiles = { 'photonlamp.png' }, groups = { matter = 1, habitat_conducts = 1, }, light_source = 14, selection_box = { - type = "fixed", + type = 'fixed', fixed = { -0.2, -0.2, -0.2, 0.2, 0.2, 0.2 }, }, collision_box = { - type = "fixed", + type = 'fixed', fixed = { -0.2, -0.2, -0.2, 0.2, 0.2, 0.2 }, }, - use_texture_alpha = "clip", + use_texture_alpha = 'clip', }) -minetest.register_craft({ - output = "sbz_decor:photonlamp", +minetest.register_craft { + output = 'sbz_decor:photonlamp', recipe = { - { "sbz_resources:matter_plate", "sbz_resources:emitter_imitator", "sbz_resources:matter_plate" }, - { "sbz_resources:emitter_imitator", "sbz_resources:matter_blob", "sbz_resources:emitter_imitator" }, - { "sbz_resources:matter_plate", "sbz_resources:emitter_imitator", "sbz_resources:matter_plate" } - } -}) - + { 'sbz_resources:matter_plate', 'sbz_resources:emitter_imitator', 'sbz_resources:matter_plate' }, + { 'sbz_resources:emitter_imitator', 'sbz_resources:matter_blob', 'sbz_resources:emitter_imitator' }, + { 'sbz_resources:matter_plate', 'sbz_resources:emitter_imitator', 'sbz_resources:matter_plate' }, + }, +} -minetest.register_node("sbz_decor:factory_floor", unifieddyes.def { - description = "Factory Floor", - tiles = { "factory_floor.png" }, - groups = { matter = 1, cracky = 3, explody = 3, moss_growable = 1 }, - sunlight_propagates = true, - walkable = true, - sounds = sbz_api.sounds.matter(), -}) -minetest.register_craft({ - output = "sbz_decor:factory_floor 2", - type = "shapeless", - recipe = { - "sbz_resources:matter_blob", - "sbz_resources:matter_blob", - "sbz_resources:matter_blob", - "sbz_resources:matter_blob" +minetest.register_node( + 'sbz_decor:factory_floor', + unifieddyes.def { + description = 'Factory Floor', + tiles = { 'factory_floor.png' }, + groups = { matter = 1, cracky = 3, explody = 3, moss_growable = 1 }, + sunlight_propagates = true, + walkable = true, + sounds = sbz_api.sounds.matter(), } -}) +) +minetest.register_craft { + output = 'sbz_decor:factory_floor 2', + type = 'shapeless', + recipe = { + 'sbz_resources:matter_blob', + 'sbz_resources:matter_blob', + 'sbz_resources:matter_blob', + 'sbz_resources:matter_blob', + }, +} -stairs.register("sbz_decor:factory_floor", { - stair_front = "factory_floor_sf.png", - stair_side = "factory_floor_ss.png", - stair_cross = "factory_floor_sc.png" +stairs.register('sbz_decor:factory_floor', { + stair_front = 'factory_floor_sf.png', + stair_side = 'factory_floor_ss.png', + stair_cross = 'factory_floor_sc.png', }) -minetest.register_node("sbz_decor:factory_floor_tiling", unifieddyes.def { - description = "Factory Floor (Tiled)", - tiles = { "factory_floor_tiling.png" }, - groups = { matter = 1, cracky = 3, explody = 3, moss_growable = 1, ud_param2_colorable = 1 }, - sunlight_propagates = true, - walkable = true, - sounds = sbz_api.sounds.matter(), -}) +minetest.register_node( + 'sbz_decor:factory_floor_tiling', + unifieddyes.def { + description = 'Factory Floor (Tiled)', + tiles = { 'factory_floor_tiling.png' }, + groups = { matter = 1, cracky = 3, explody = 3, moss_growable = 1, ud_param2_colorable = 1 }, + sunlight_propagates = true, + walkable = true, + sounds = sbz_api.sounds.matter(), + } +) -stairs.register("sbz_decor:factory_floor_tiling") +stairs.register 'sbz_decor:factory_floor_tiling' -minetest.register_craft({ - output = "sbz_decor:factory_floor_tiling 4", - type = "shapeless", - recipe = { "sbz_decor:factory_floor", "sbz_decor:factory_floor", "sbz_decor:factory_floor", "sbz_decor:factory_floor" } -}) +minetest.register_craft { + output = 'sbz_decor:factory_floor_tiling 4', + type = 'shapeless', + recipe = { + 'sbz_decor:factory_floor', + 'sbz_decor:factory_floor', + 'sbz_decor:factory_floor', + 'sbz_decor:factory_floor', + }, +} -minetest.register_node("sbz_decor:factory_ventilator", { - description = "Factory Ventilator", +minetest.register_node('sbz_decor:factory_ventilator', { + description = 'Factory Ventilator', tiles = { - { name = "factory_ventilator.png", animation = { type = "vertical_frames", length = 1 } }, + { name = 'factory_ventilator.png', animation = { type = 'vertical_frames', length = 1 } }, }, groups = { matter = 1, cracky = 3, explody = 3, moss_growable = 1 }, sunlight_propagates = true, walkable = true, sounds = sbz_api.sounds.matter(), }) -minetest.register_craft({ - output = "sbz_decor:factory_ventilator", - type = "shapeless", - recipe = { "sbz_decor:factory_floor", "sbz_decor:factory_floor", "sbz_chem:lithium_powder", "sbz_chem:cobalt_powder" } -}) +minetest.register_craft { + output = 'sbz_decor:factory_ventilator', + type = 'shapeless', + recipe = { + 'sbz_decor:factory_floor', + 'sbz_decor:factory_floor', + 'sbz_chem:lithium_powder', + 'sbz_chem:cobalt_powder', + }, +} -minetest.register_node("sbz_decor:factory_warning", unifieddyes.def { - description = "Factory Warning", - tiles = { "factory_warning.png" }, - groups = { matter = 1, cracky = 3, explody = 3, moss_growable = 1 }, - sunlight_propagates = true, - walkable = true, - sounds = sbz_api.sounds.matter(), -}) -stairs.register("sbz_decor:factory_warning") -minetest.register_craft({ - output = "sbz_decor:factory_warning 4", - type = "shapeless", - recipe = { "sbz_decor:factory_floor", "sbz_decor:factory_floor", "sbz_chem:gold_powder", "sbz_chem:gold_powder" } -}) +minetest.register_node( + 'sbz_decor:factory_warning', + unifieddyes.def { + description = 'Factory Warning', + tiles = { 'factory_warning.png' }, + groups = { matter = 1, cracky = 3, explody = 3, moss_growable = 1 }, + sunlight_propagates = true, + walkable = true, + sounds = sbz_api.sounds.matter(), + } +) +stairs.register 'sbz_decor:factory_warning' +minetest.register_craft { + output = 'sbz_decor:factory_warning 4', + type = 'shapeless', + recipe = { 'sbz_decor:factory_floor', 'sbz_decor:factory_floor', 'sbz_chem:gold_powder', 'sbz_chem:gold_powder' }, +} -minetest.register_node("sbz_decor:mystery_terrarium", { - description = "Mystery Terrarium", +minetest.register_node('sbz_decor:mystery_terrarium', { + description = 'Mystery Terrarium', tiles = { - { name = "mystery_terrarium.png", animation = { type = "vertical_frames", length = 1 } }, + { name = 'mystery_terrarium.png', animation = { type = 'vertical_frames', length = 1 } }, }, - groups = { matter = 1, cracky = 3, explody = 3}, + groups = { matter = 1, cracky = 3, explody = 3 }, sunlight_propagates = true, walkable = true, sounds = sbz_api.sounds.matter(), }) -minetest.register_craft({ - output = "sbz_decor:mystery_terrarium", - type = "shapeless", - recipe = {"sbz_bio:habitat_regulator", "sbz_bio:screen_inverter_potion", "sbz_chem:thorium_fluid_cell"} -}) +minetest.register_craft { + output = 'sbz_decor:mystery_terrarium', + type = 'shapeless', + recipe = { 'sbz_bio:habitat_regulator', 'sbz_bio:screen_inverter_potion', 'sbz_chem:thorium_fluid_cell' }, +} -minetest.register_node("sbz_decor:large_server_rack", { - description = "Large Server Rack", - tiles = { -- how do i make it turn depending on how the player places it? - { name = "large_server_rack_back.png" }, - { name = "large_server_rack_back.png" }, - { name = "large_server_rack.png", animation = { type = "vertical_frames", length = 3 } }, - { name = "large_server_rack_back.png" }, - { name = "large_server_rack_back.png" }, - { name = "large_server_rack_back.png" }, - }, - groups = { matter = 1, cracky = 3, explody = 3}, - light_source = 10, - sunlight_propagates = true, - walkable = true, - sounds = sbz_api.sounds.matter(), -}) -minetest.register_craft({ - output = "sbz_decor:large_server_rack", - type = "shapeless", - recipe = {"sbz_resources:simple_processor 100", "sbz_logic:lua_controller_off", "sbz_resources:ram_stick_1mb 100"} -}) +minetest.register_node( + 'sbz_decor:large_server_rack', + unifieddyes.def { + description = 'Large Server Rack', + info_extra = 'Just decoration.', + tiles = { + { name = 'large_server_rack_back.png' }, + { name = 'large_server_rack_back.png' }, + { name = 'large_server_rack.png', animation = { type = 'vertical_frames', length = 3 } }, + { name = 'large_server_rack_back.png' }, + { name = 'large_server_rack_back.png' }, + { name = 'large_server_rack_back.png' }, + }, + paramtype2 = 'colorfacedir', + groups = { matter = 1, cracky = 3, explody = 3 }, + light_source = 10, + sunlight_propagates = true, + sounds = sbz_api.sounds.matter(), + } +) -local MP = minetest.get_modpath("sbz_decor") -dofile(MP .. "/signs.lua") -dofile(MP .. "/cnc.lua") +minetest.register_craft { + output = 'sbz_decor:large_server_rack', + type = 'shapeless', + recipe = { 'sbz_resources:matter_blob', 'sbz_resources:luanium' }, +} + +local MP = minetest.get_modpath 'sbz_decor' +dofile(MP .. '/signs.lua') +dofile(MP .. '/cnc.lua') -- now... Ladders -- inspired by what i saw from mtg ladders @@ -153,7 +175,7 @@ local ladder_autoplace_limit = 16 local get_ladder_on_place = function(ladder_name) return sbz_api.on_place_precedence(function(stack, placer, pointed, recursed) if (recursed or 0) > ladder_autoplace_limit then return end - if pointed.type == "node" then + if pointed.type == 'node' then local target = pointed.under local node = core.get_node(target) if node.name == ladder_name then @@ -162,8 +184,8 @@ local get_ladder_on_place = function(ladder_name) pointed.under = vector.add(pointed.under, up) pointed.above = vector.add(pointed.above, up) if core.get_node(pointed.under).name == ladder_name then - local result = minetest.registered_nodes[ladder_name].on_place(stack, placer, pointed, - (recursed or 0) + 1) + local result = + minetest.registered_nodes[ladder_name].on_place(stack, placer, pointed, (recursed or 0) + 1) return result end return core.item_place_node(stack, placer, pointed, node.param2) @@ -173,93 +195,109 @@ local get_ladder_on_place = function(ladder_name) end) end -core.register_node("sbz_decor:ladder", unifieddyes.def { - description = "Matter Ladder", - drawtype = "nodebox", - node_box = { -- nodebox inspired by that one 3d ladders mod, but i made this myself with nodebox editor - type = "fixed", - fixed = { - { -0.5, -0.5, 0.375, -0.375, 0.5, 0.5 }, -- NodeBox1 - { 0.375, -0.5, 0.375, 0.5, 0.5, 0.5 }, -- NodeBox3 - { -0.375, 0.3125, 0.375, 0.375, 0.4375, 0.5 }, -- NodeBox5 - { -0.375, 0.0625, 0.375, 0.375, 0.1875, 0.5 }, -- NodeBox8 - { -0.375, -0.1875, 0.375, 0.375, -0.0625, 0.5 }, -- NodeBox9 - { -0.375, -0.4375, 0.375, 0.375, -0.3125, 0.5 }, -- NodeBox10 - } - }, - selection_box = { - type = "fixed", - fixed = { - -8 / 16, -8 / 16, 3 / 16, 8 / 16, 8 / 16, 8 / 16 - } - }, - tiles = { "matter_blob.png" }, - inventory_image = "ladder.png", - groups = { - matter = 3, - explody = 3, - habitat_conducts = 1, - }, - paramtype = "light", - paramtype2 = "facedir", - sunlight_propagates = true, - on_place = get_ladder_on_place("sbz_decor:ladder"), - node_placement_prediction = "", - climbable = true, -}) +core.register_node( + 'sbz_decor:ladder', + unifieddyes.def { + description = 'Matter Ladder', + drawtype = 'nodebox', + node_box = { -- nodebox inspired by that one 3d ladders mod, but i made this myself with nodebox editor + type = 'fixed', + fixed = { + { -0.5, -0.5, 0.375, -0.375, 0.5, 0.5 }, -- NodeBox1 + { 0.375, -0.5, 0.375, 0.5, 0.5, 0.5 }, -- NodeBox3 + { -0.375, 0.3125, 0.375, 0.375, 0.4375, 0.5 }, -- NodeBox5 + { -0.375, 0.0625, 0.375, 0.375, 0.1875, 0.5 }, -- NodeBox8 + { -0.375, -0.1875, 0.375, 0.375, -0.0625, 0.5 }, -- NodeBox9 + { -0.375, -0.4375, 0.375, 0.375, -0.3125, 0.5 }, -- NodeBox10 + }, + }, + selection_box = { + type = 'fixed', + fixed = { + -8 / 16, + -8 / 16, + 3 / 16, + 8 / 16, + 8 / 16, + 8 / 16, + }, + }, + tiles = { 'matter_blob.png' }, + inventory_image = 'ladder.png', + groups = { + matter = 3, + explody = 3, + habitat_conducts = 1, + }, + paramtype = 'light', + paramtype2 = 'facedir', + sunlight_propagates = true, + on_place = get_ladder_on_place 'sbz_decor:ladder', + node_placement_prediction = '', + climbable = true, + } +) -core.register_node("sbz_decor:antimatter_ladder", unifieddyes.def { - description = "Antimatter Ladder", - drawtype = "nodebox", - node_box = { -- nodebox inspired by that one 3d ladders mod, but i made this myself with nodebox editor - type = "fixed", - fixed = { - { -0.5, -0.5, 0.375, -0.375, 0.5, 0.5 }, -- NodeBox1 - { 0.375, -0.5, 0.375, 0.5, 0.5, 0.5 }, -- NodeBox3 - { -0.375, 0.3125, 0.375, 0.375, 0.4375, 0.5 }, -- NodeBox5 - { -0.375, 0.0625, 0.375, 0.375, 0.1875, 0.5 }, -- NodeBox8 - { -0.375, -0.1875, 0.375, 0.375, -0.0625, 0.5 }, -- NodeBox9 - { -0.375, -0.4375, 0.375, 0.375, -0.3125, 0.5 }, -- NodeBox10 - } - }, - selection_box = { - type = "fixed", - fixed = { - -8 / 16, -8 / 16, 3 / 16, 8 / 16, 8 / 16, 8 / 16 - } - }, - tiles = { "antimatter_blob.png" }, - inventory_image = "antimatter_ladder.png", - groups = { - matter = 3, - explody = 3, - habitat_conducts = 1, - }, - light_source = 3, - paramtype = "light", - paramtype2 = "colorfacedir", --"facedir", - sunlight_propagates = true, - on_place = get_ladder_on_place("sbz_decor:antimatter_ladder"), - node_placement_prediction = "", - climbable = true, -}) +core.register_node( + 'sbz_decor:antimatter_ladder', + unifieddyes.def { + description = 'Antimatter Ladder', + drawtype = 'nodebox', + node_box = { -- nodebox inspired by that one 3d ladders mod, but i made this myself with nodebox editor + type = 'fixed', + fixed = { + { -0.5, -0.5, 0.375, -0.375, 0.5, 0.5 }, -- NodeBox1 + { 0.375, -0.5, 0.375, 0.5, 0.5, 0.5 }, -- NodeBox3 + { -0.375, 0.3125, 0.375, 0.375, 0.4375, 0.5 }, -- NodeBox5 + { -0.375, 0.0625, 0.375, 0.375, 0.1875, 0.5 }, -- NodeBox8 + { -0.375, -0.1875, 0.375, 0.375, -0.0625, 0.5 }, -- NodeBox9 + { -0.375, -0.4375, 0.375, 0.375, -0.3125, 0.5 }, -- NodeBox10 + }, + }, + selection_box = { + type = 'fixed', + fixed = { + -8 / 16, + -8 / 16, + 3 / 16, + 8 / 16, + 8 / 16, + 8 / 16, + }, + }, + tiles = { 'antimatter_blob.png' }, + inventory_image = 'antimatter_ladder.png', + groups = { + matter = 3, + explody = 3, + habitat_conducts = 1, + }, + light_source = 3, + paramtype = 'light', + paramtype2 = 'colorfacedir', --"facedir", + sunlight_propagates = true, + on_place = get_ladder_on_place 'sbz_decor:antimatter_ladder', + node_placement_prediction = '', + climbable = true, + } +) -core.register_alias_force("sbz_decor:anitmatter_ladder", "sbz_decor:antimatter_ladder") +core.register_alias_force('sbz_decor:anitmatter_ladder', 'sbz_decor:antimatter_ladder') core.register_craft { - output = "sbz_decor:antimatter_ladder 12", + output = 'sbz_decor:antimatter_ladder 12', recipe = { - { "sbz_resources:antimatter_blob", "", "sbz_resources:antimatter_blob", }, - { "sbz_resources:antimatter_blob", "sbz_resources:antimatter_blob", "sbz_resources:antimatter_blob", }, - { "sbz_resources:antimatter_blob", "", "sbz_resources:antimatter_blob", }, - } + { 'sbz_resources:antimatter_blob', '', 'sbz_resources:antimatter_blob' }, + { 'sbz_resources:antimatter_blob', 'sbz_resources:antimatter_blob', 'sbz_resources:antimatter_blob' }, + { 'sbz_resources:antimatter_blob', '', 'sbz_resources:antimatter_blob' }, + }, } core.register_craft { - output = "sbz_decor:ladder 12", + output = 'sbz_decor:ladder 12', recipe = { - { "sbz_resources:matter_blob", "", "sbz_resources:matter_blob", }, - { "sbz_resources:matter_blob", "sbz_resources:matter_blob", "sbz_resources:matter_blob", }, - { "sbz_resources:matter_blob", "", "sbz_resources:matter_blob", }, - } + { 'sbz_resources:matter_blob', '', 'sbz_resources:matter_blob' }, + { 'sbz_resources:matter_blob', 'sbz_resources:matter_blob', 'sbz_resources:matter_blob' }, + { 'sbz_resources:matter_blob', '', 'sbz_resources:matter_blob' }, + }, } diff --git a/mods/sbz_instatube/init.lua b/mods/sbz_instatube/init.lua index 1a56b2a3..1dd5e8ac 100644 --- a/mods/sbz_instatube/init.lua +++ b/mods/sbz_instatube/init.lua @@ -243,7 +243,7 @@ local instatube_insert_object = function(pos, _, stack, _, owner, ordering) if machine.tube.insert_object then stack = machine.tube.insert_object(mpos, mnode, stack, { x = 0, y = 0, z = 0, speed = 1 }, owner) end - else -- its a tupe, dump the stack in + else -- its a tube, dump the stack in local entity = pipeworks.tube_inject_item( mpos, vector.subtract(mpos, machine.dir), @@ -831,6 +831,7 @@ instatube.show_network = function(p1, p2, net) end end +--- This is a function meant for debugging instatube.get_machine = function(pos, net_id) local net = instatube.networks[net_id] for k, v in pairs(net.machines) do diff --git a/mods/sbz_meteorites/visualiser.lua b/mods/sbz_meteorites/visualiser.lua index 239d9158..19ff0ad8 100644 --- a/mods/sbz_meteorites/visualiser.lua +++ b/mods/sbz_meteorites/visualiser.lua @@ -1,25 +1,27 @@ -sbz_api.register_machine("sbz_meteorites:meteorite_radar", { +sbz_api.register_machine('sbz_meteorites:meteorite_radar', { disallow_pipeworks = true, - description = "Meteorite Radar", - drawtype = "mesh", - paramtype = "light", - mesh = "meteorite_radar.obj", - tiles = { "meteorite_radar.png" }, + description = 'Meteorite Radar', + info_extra = "Detects if meteorites are nearby. Uses 10Cj if there aren't, uses 40Cj if there are.", + drawtype = 'mesh', + paramtype = 'light', + mesh = 'meteorite_radar.obj', + tiles = { 'meteorite_radar.png' }, collision_box = { - type = "fixed", - fixed = { -0.5, -0.5, -0.5, 0.5, 0.25, 0.5 } + type = 'fixed', + fixed = { -0.5, -0.5, -0.5, 0.5, 0.25, 0.5 }, }, selection_box = { - type = "fixed", - fixed = { -0.5, -0.5, -0.5, 0.5, 0.25, 0.5 } + type = 'fixed', + fixed = { -0.5, -0.5, -0.5, 0.5, 0.25, 0.5 }, }, groups = { matter = 1 }, - power_needed = 20, - action_interval = 0, + power_needed = 40, on_construct = function(pos) - minetest.sound_play({ name = "machine_build" }, { pos = pos }) + minetest.sound_play({ name = 'machine_build' }, { pos = pos }) end, action = function(radar_pos) + local power_consume = 10 + local players = {} local meteorites = {} local attractors = {} @@ -28,31 +30,28 @@ sbz_api.register_machine("sbz_meteorites:meteorite_radar", { if obj then local entity = obj:get_luaentity() if not entity then - if obj:is_player() then - table.insert(players, obj:get_player_name()) - end - elseif entity.name == "sbz_meteorites:meteorite" then + if obj:is_player() then table.insert(players, obj:get_player_name()) end + elseif entity.name == 'sbz_meteorites:meteorite' then table.insert(meteorites, obj) - elseif entity.name == "sbz_meteorites:gravitational_attractor_entity" then + elseif entity.name == 'sbz_meteorites:gravitational_attractor_entity' then table.insert(entity.type < 0 and repulsors or attractors, vector.round(obj:get_pos())) end end end if #meteorites > 0 then - minetest.add_particle({ + minetest.add_particle { pos = radar_pos + vector.new(0, 1.5, 0), expiration_time = 1, size = 10, - texture = "antenna.png", - animation = { type = "vertical_frames", aspect_width = 18, aspect_height = 18, length = 0.5 }, - glow = 14 - }) + texture = 'antenna.png', + animation = { type = 'vertical_frames', aspect_width = 18, aspect_height = 18, length = 0.5 }, + glow = 14, + } - minetest.sound_play( - { name = "alarm", gain = 0.7 }, - { pos = radar_pos, max_hear_distance = 64 } - ) + minetest.sound_play({ name = 'alarm', gain = 0.7 }, { pos = radar_pos, max_hear_distance = 64 }) + power_consume = 40 end + for _, obj in ipairs(meteorites) do obj:get_luaentity():show_waypoint() local pos = obj:get_pos() @@ -69,27 +68,28 @@ sbz_api.register_machine("sbz_meteorites:meteorite_radar", { end local collides = minetest.registered_nodes[minetest.get_node(vector.round(pos)).name].walkable for _, player in ipairs(players) do - minetest.add_particle({ + minetest.add_particle { pos = pos, expiration_time = 1, size = collides and 50 or 10, - texture = "visualiser_trail.png", - animation = { type = "vertical_frames", aspect_width = 8, aspect_height = 8, length = 0.2 }, + texture = 'visualiser_trail.png', + animation = { type = 'vertical_frames', aspect_width = 8, aspect_height = 8, length = 0.2 }, glow = 14, - playername = player - }) + playername = player, + } end if collides then break end end end - end + return power_consume + end, }) core.register_craft { - output = "sbz_meteorites:meteorite_radar", + output = 'sbz_meteorites:meteorite_radar', recipe = { - { "", "sbz_chem:titanium_alloy_ingot", "" }, - { "", "sbz_chem:titanium_alloy_ingot", "" }, - { "sbz_resources:reinforced_matter", "sbz_resources:prediction_circuit", "sbz_resources:reinforced_matter" } - } + { '', 'sbz_chem:titanium_alloy_ingot', '' }, + { '', 'sbz_chem:titanium_alloy_ingot', '' }, + { 'sbz_resources:reinforced_matter', 'sbz_resources:prediction_circuit', 'sbz_resources:reinforced_matter' }, + }, } diff --git a/mods/sbz_multiblocks/large_liquid_storage.lua b/mods/sbz_multiblocks/large_liquid_storage.lua index 4456ed66..a84d96a8 100644 --- a/mods/sbz_multiblocks/large_liquid_storage.lua +++ b/mods/sbz_multiblocks/large_liquid_storage.lua @@ -3,15 +3,17 @@ local storage_per_node = 200 -- 5x5x5 25000 liquid sources local function get_llsc_formspec(pos) local meta = core.get_meta(pos) - local storage_size = meta:get_int("storage_size") + local storage_size = meta:get_int 'storage_size' if storage_size == 0 then storage_size = 2 - meta:set_int("storage_size", 2) + meta:set_int('storage_size', 2) end - if meta:get_int("set_up") == 1 then - core.registered_nodes[sbz_api.get_node_force(pos).name].on_liquid_inv_update(pos, - core.deserialize(meta:get_string("liquid_inv"))) - return "" + if meta:get_int 'set_up' == 1 then + core.registered_nodes[sbz_api.get_node_force(pos).name].on_liquid_inv_update( + pos, + core.deserialize(meta:get_string 'liquid_inv') + ) + return '' else local fs = ([[ formspec_version[7] @@ -26,12 +28,14 @@ label[3.2,3.3;3] label[4.7,3.3;4] label[6.25,3.3;5] ]]):format(storage_size) - if #meta:get_string("errmsg") ~= 0 then - fs = fs .. - string.format("textarea[0.5,4;8,2;;Error message when trying to form multiblock:;%s]", - meta:get_string("errmsg")) + if #meta:get_string 'errmsg' ~= 0 then + fs = fs + .. string.format( + 'textarea[0.5,4;8,2;;Error message when trying to form multiblock:;%s]', + meta:get_string 'errmsg' + ) else - fs = fs .. ("label[0.5,4;=> %s liquid source(s)]"):format(storage_size ^ 3 * storage_per_node) + fs = fs .. ('label[0.5,4;=> %s liquid source(s)]'):format(storage_size ^ 3 * storage_per_node) end return fs end @@ -40,9 +44,9 @@ end local cached_schems = {} local h = core.hash_node_position local function make_schem(pos, meta, node) - local storage_size = (meta and meta.storage_size) or core.get_meta(pos):get_int("storage_size") + local storage_size = (meta and meta.storage_size) or core.get_meta(pos):get_int 'storage_size' local param2 = (node and node.param2) or sbz_api.get_or_load_node(pos).param2 - local orientation = core.facedir_to_dir(param2 - (core.strip_param2_color(param2, "colorfacedir") or 0)) + local orientation = core.facedir_to_dir(param2 - (core.strip_param2_color(param2, 'colorfacedir') or 0)) if cached_schems[storage_size] and cached_schems[storage_size][h(orientation)] then return cached_schems[storage_size][h(orientation)] end @@ -56,58 +60,47 @@ local function make_schem(pos, meta, node) -- most boring code ever local vn = vector.new - local edge = "sbz_multiblocks:large_liquid_storage_casing_edge" + local edge = 'sbz_multiblocks:large_liquid_storage_casing_edge' -- there is at least 1 redundant loop here definitely xD - for _, x in ipairs({ 0, storage_size + 1 }) do - for _, y in ipairs({ 0, storage_size + 1 }) do + for _, x in ipairs { 0, storage_size + 1 } do + for _, y in ipairs { 0, storage_size + 1 } do for z = 0, storage_size + 1 do - if not (y == 0 and z == 0 and x == 0) then - schemdata[h(vn(x, y, z))] = edge - end + if not (y == 0 and z == 0 and x == 0) then schemdata[h(vn(x, y, z))] = edge end end end end - for _, y in ipairs({ 0, storage_size + 1 }) do - for _, z in ipairs({ 0, storage_size + 1 }) do + for _, y in ipairs { 0, storage_size + 1 } do + for _, z in ipairs { 0, storage_size + 1 } do for x = 0, storage_size + 1 do - if not (y == 0 and z == 0 and x == 0) then - schemdata[h(vn(x, y, z))] = edge - end + if not (y == 0 and z == 0 and x == 0) then schemdata[h(vn(x, y, z))] = edge end end end end - for _, z in ipairs({ 0, storage_size + 1 }) do - for _, y in ipairs({ 0, storage_size + 1 }) do + for _, z in ipairs { 0, storage_size + 1 } do + for _, y in ipairs { 0, storage_size + 1 } do for x = 0, storage_size + 1 do - if not (y == 0 and z == 0 and x == 0) then - schemdata[h(vn(x, y, z))] = edge - end + if not (y == 0 and z == 0 and x == 0) then schemdata[h(vn(x, y, z))] = edge end end end end - for _, z in ipairs({ 0, storage_size + 1 }) do - for _, x in ipairs({ 0, storage_size + 1 }) do + for _, z in ipairs { 0, storage_size + 1 } do + for _, x in ipairs { 0, storage_size + 1 } do for y = 0, storage_size + 1 do - if not (y == 0 and z == 0 and x == 0) then - schemdata[h(vn(x, y, z))] = edge - end + if not (y == 0 and z == 0 and x == 0) then schemdata[h(vn(x, y, z))] = edge end end end end - for _, x in ipairs({ 0, storage_size + 1 }) do - for _, z in ipairs({ 0, storage_size + 1 }) do + for _, x in ipairs { 0, storage_size + 1 } do + for _, z in ipairs { 0, storage_size + 1 } do for y = 0, storage_size + 1 do - if not (y == 0 and z == 0 and x == 0) then - schemdata[h(vn(x, y, z))] = edge - end + if not (y == 0 and z == 0 and x == 0) then schemdata[h(vn(x, y, z))] = edge end end end end - - local side = "sbz_multiblocks:large_liquid_storage_casing" - for _, z in ipairs({ 0, storage_size + 1 }) do + local side = 'sbz_multiblocks:large_liquid_storage_casing' + for _, z in ipairs { 0, storage_size + 1 } do for x = 1, storage_size do for y = 1, storage_size do schemdata[h(vn(x, y, z))] = side @@ -115,7 +108,7 @@ local function make_schem(pos, meta, node) end end - for _, x in ipairs({ 0, storage_size + 1 }) do + for _, x in ipairs { 0, storage_size + 1 } do for z = 1, storage_size do for y = 1, storage_size do schemdata[h(vn(x, y, z))] = side @@ -123,7 +116,7 @@ local function make_schem(pos, meta, node) end end - for _, y in ipairs({ 0, storage_size + 1 }) do + for _, y in ipairs { 0, storage_size + 1 } do for z = 1, storage_size do for x = 1, storage_size do schemdata[h(vn(x, y, z))] = side @@ -132,150 +125,164 @@ local function make_schem(pos, meta, node) end schem = sbz_api.multiblocks.rotate_schematic(schem, orientation) - if not cached_schems[storage_size] then - cached_schems[storage_size] = {} - end + if not cached_schems[storage_size] then cached_schems[storage_size] = {} end cached_schems[storage_size][h(orientation)] = schem return schem end -local default_inv = minetest.serialize({ +local default_inv = minetest.serialize { max_count_in_each_stack = 0, [1] = { - name = "any", + name = 'any', count = 0, can_change_name = true, }, -}) +} -core.register_node("sbz_multiblocks:large_liquid_storage_controller", unifieddyes.def { - description = "Large Liquid Storage Controller", - groups = { - matter = 1, - multiblock_controller = 1, - fluid_pipe_connects = 1, fluid_pipe_stores = 1, ui_fluid = 1 - }, - paramtype2 = "colorfacedir", - tiles = { - "large_liquid_storage_controller_top.png", - "large_liquid_storage_controller_top.png", - "large_liquid_storage_controller_sides.png", - "large_liquid_storage_controller_sides.png", - "large_liquid_storage_controller_sides.png", - "large_liquid_storage_controller_back.png", - }, - light_source = 3, - on_construct = function(pos) - local meta = core.get_meta(pos) - meta:set_string("formspec", get_llsc_formspec(pos)) - meta:set_string("liquid_inv", default_inv) - end, - on_receive_fields = function(pos, _, fields, sender) - local meta = core.get_meta(pos) - local is_multiblock = meta:get_int("set_up") == 1 - if not is_multiblock then - if fields.storage_size then - local scrollbar_event = core.explode_scrollbar_event(fields.storage_size) - if scrollbar_event.type == "CHG" and scrollbar_event.value <= 5 and scrollbar_event.value >= 2 then - meta:set_int("storage_size", scrollbar_event.value) +core.register_node( + 'sbz_multiblocks:large_liquid_storage_controller', + unifieddyes.def { + description = 'Large Liquid Storage Controller', + groups = { + matter = 1, + multiblock_controller = 1, + fluid_pipe_connects = 1, + fluid_pipe_stores = 1, + ui_fluid = 1, + }, + paramtype2 = 'colorfacedir', + tiles = { + 'large_liquid_storage_controller_top.png', + 'large_liquid_storage_controller_top.png', + 'large_liquid_storage_controller_sides.png', + 'large_liquid_storage_controller_sides.png', + 'large_liquid_storage_controller_sides.png', + 'large_liquid_storage_controller_back.png', + }, + light_source = 3, + on_construct = function(pos) + local meta = core.get_meta(pos) + meta:set_string('formspec', get_llsc_formspec(pos)) + meta:set_string('liquid_inv', default_inv) + end, + on_receive_fields = function(pos, _, fields, sender) + local meta = core.get_meta(pos) + local is_multiblock = meta:get_int 'set_up' == 1 + if not is_multiblock then + if fields.storage_size then + local scrollbar_event = core.explode_scrollbar_event(fields.storage_size) + if scrollbar_event.type == 'CHG' and scrollbar_event.value <= 5 and scrollbar_event.value >= 2 then + meta:set_int('storage_size', scrollbar_event.value) + end end - end - if fields.show_ghosts then - local default_expiration = 8 - local last_used = meta:get_int("last_used_build_helper") - if os.difftime(os.time(), last_used) >= default_expiration then + if fields.show_ghosts then + local default_expiration = 8 + local last_used = meta:get_int 'last_used_build_helper' + if os.difftime(os.time(), last_used) >= default_expiration then + local schem = make_schem(pos) + sbz_api.multiblocks.draw_schematic(pos, schem) + meta:set_int('last_used_build_helper', os.time()) + else + core.chat_send_player( + sender:get_player_name(), + 'You need to wait ' + .. (default_expiration - os.difftime(os.time(), last_used)) + .. ' seconds before showing build plan again.' + ) + end + end + if fields.form_multiblock then local schem = make_schem(pos) - sbz_api.multiblocks.draw_schematic(pos, schem) - meta:set_int("last_used_build_helper", os.time()) - else - core.chat_send_player(sender:get_player_name(), - "You need to wait " .. - (default_expiration - os.difftime(os.time(), last_used)) .. - " seconds before showing build plan again.") + local result = sbz_api.multiblocks.form_multiblock(pos, schem) + if result.success == false then + meta:set_string('errmsg', result.errmsg) + else + meta:set_int('set_up', 1) + meta:set_string( + 'liquid_inv', + core.serialize { + max_count_in_each_stack = meta:get_int 'storage_size' ^ 3 * storage_per_node, + [1] = { + name = 'any', + count = 0, + can_change_name = true, + }, + } + ) + meta:set_string('infotext', 'Waiting for a liquid...') + end end end - if fields.form_multiblock then - local schem = make_schem(pos) - local result = sbz_api.multiblocks.form_multiblock(pos, schem) - if result.success == false then - meta:set_string("errmsg", result.errmsg) - else - meta:set_int("set_up", 1) - meta:set_string("liquid_inv", core.serialize { - max_count_in_each_stack = meta:get_int("storage_size") ^ 3 * storage_per_node, - [1] = { - name = "any", - count = 0, - can_change_name = true, - } - }) - meta:set_string("infotext", "Waiting for a liquid...") - end + local newfs = get_llsc_formspec(pos) + if newfs ~= '' then meta:set_string('formspec', newfs) end + end, + on_multiblock_break = function(pos, meta) + meta:set_int('set_up', 0) + meta:set_string('formspec', get_llsc_formspec(pos)) + meta:set_string('liquid_inv', default_inv) + end, + get_schematic = make_schem, + after_dig_node = sbz_api.multiblocks.after_dig_controller 'sbz_multiblocks:large_liquid_storage_controller', + before_movenode = sbz_api.multiblocks.before_movenode, + on_liquid_inv_update = function(pos, lqinv) + local meta = minetest.get_meta(pos) + if lqinv[1].name == 'any' then + meta:set_string('infotext', 'Waiting for a liquid...') + return end - end - local newfs = get_llsc_formspec(pos) - if newfs ~= "" then - meta:set_string("formspec", newfs) - end - end, - on_multiblock_break = function(pos, meta) - meta:set_int("set_up", 0) - meta:set_string("formspec", get_llsc_formspec(pos)) - meta:set_string("liquid_inv", default_inv) - core.debug(dump(debug.traceback())) - end, - get_schematic = make_schem, - after_dig_node = sbz_api.multiblocks.after_dig_controller("sbz_multiblocks:large_liquid_storage_controller"), - before_movenode = sbz_api.multiblocks.before_movenode, - on_liquid_inv_update = function(pos, lqinv) - local meta = minetest.get_meta(pos) - if lqinv[1].name == "any" then - meta:set_string("infotext", "Waiting for a liquid...") - return; - end - local def = minetest.registered_nodes[lqinv[1].name] - local desc = string.gsub(def.short_description or def.description or lqinv[1].name, " Source", "") - meta:set_string("infotext", ("Storing %s : %s/%s"):format(desc, lqinv[1].count, lqinv.max_count_in_each_stack)) - meta:set_string("formspec", sbz_api.liquid_storage_fs(lqinv[1].count, lqinv.max_count_in_each_stack)) - end, -}) + local def = minetest.registered_nodes[lqinv[1].name] + local desc = string.gsub(def.short_description or def.description or lqinv[1].name, ' Source', '') + meta:set_string( + 'infotext', + ('Storing %s : %s/%s'):format(desc, lqinv[1].count, lqinv.max_count_in_each_stack) + ) + meta:set_string('formspec', sbz_api.liquid_storage_fs(lqinv[1].count, lqinv.max_count_in_each_stack)) + end, + } +) -core.register_node("sbz_multiblocks:large_liquid_storage_casing", unifieddyes.def { - description = "Large Liquid Storage Casing", - groups = { - matter = 1, - wallsharing = 1, - ui_fluid = 1, - }, - info_extra = "Or \"Dark Stained Colorium Glass\" If you are into decorating", - drawtype = "glasslike_framed", - paramtype = "light", - paramtype2 = "color", - tiles = { - "large_liquid_storage_casing.png", - "large_liquid_storage_casing_inner.png", - }, - use_texture_alpha = "blend", - light_source = 3, - after_dig_node = sbz_api.multiblocks.after_dig, - before_movenode = sbz_api.multiblocks.before_movenode, -}) +core.register_node( + 'sbz_multiblocks:large_liquid_storage_casing', + unifieddyes.def { + description = 'Large Liquid Storage Casing', + groups = { + matter = 1, + wallsharing = 1, + ui_fluid = 1, + }, + info_extra = 'Or "Dark Stained Colorium Glass" If you are into decorating', + drawtype = 'glasslike_framed', + paramtype = 'light', + paramtype2 = 'color', + tiles = { + 'large_liquid_storage_casing.png', + 'large_liquid_storage_casing_inner.png', + }, + use_texture_alpha = 'blend', + light_source = 3, + after_dig_node = sbz_api.multiblocks.after_dig, + before_movenode = sbz_api.multiblocks.before_movenode, + } +) -core.register_node("sbz_multiblocks:large_liquid_storage_casing_edge", unifieddyes.def { - description = "Large Liquid Storage Edge Casing ", - groups = { - matter = 1, - wallsharing = 1, - ui_fluid = 1 - }, - drawtype = "glasslike_framed", - paramtype = "light", - paramtype2 = "color", - tiles = { - "large_liquid_storage_casing_edge.png", - "large_liquid_storage_casing_edge_inner.png", - }, - light_source = 3, - after_dig_node = sbz_api.multiblocks.after_dig, - before_movenode = sbz_api.multiblocks.before_movenode, -}) +core.register_node( + 'sbz_multiblocks:large_liquid_storage_casing_edge', + unifieddyes.def { + description = 'Large Liquid Storage Edge Casing ', + groups = { + matter = 1, + wallsharing = 1, + ui_fluid = 1, + }, + drawtype = 'glasslike_framed', + paramtype = 'light', + paramtype2 = 'color', + tiles = { + 'large_liquid_storage_casing_edge.png', + 'large_liquid_storage_casing_edge_inner.png', + }, + light_source = 3, + after_dig_node = sbz_api.multiblocks.after_dig, + before_movenode = sbz_api.multiblocks.before_movenode, + } +) diff --git a/mods/sbz_pipeworks/autocrafter.lua b/mods/sbz_pipeworks/autocrafter.lua index 5d08fa3f..d25066f7 100644 --- a/mods/sbz_pipeworks/autocrafter.lua +++ b/mods/sbz_pipeworks/autocrafter.lua @@ -112,7 +112,7 @@ end local function reserved_items_formspec(pos) local fs = {} - local offset = { 0.22, 4 } + local offset = { 0.22, 4.3 } local reserved_slots = get_reserved_slots_or_reserve_them(pos) for i = 1, 9 do local name = reserved_slots[i] @@ -306,6 +306,7 @@ local function on_output_change(pos, inventory, stack) else local input = minetest.get_craft_recipe(stack:get_name()) if not input.items or input.type ~= 'normal' then return end + local items, width = normalize(input.items), input.width local item_idx, width_idx = 1, 1 for i = 1, 9 do @@ -328,15 +329,16 @@ end local function update_meta(pos, meta) reserve_slots(pos, meta) local fs = 'formspec_version[7]' - .. 'size[11.4,10.5]' + .. 'size[11.4,11]' .. 'list[context;recipe;0.22,0.22;3,3;]' .. 'image[4,1.45;1,1;[combine:16x16^[noalpha^[colorize:#141318:255]' .. 'list[context;output;4,1.45;1,1;]' + .. 'item_image[4,2.7;1,1;sbz_resources:simple_crafting_processor]' .. 'list[context;processor;4,2.7;1,1;]' .. 'list[context;dst;5.28,0.22;4,3;]' .. reserved_items_formspec(pos) - .. 'list[context;src;0.22,4;9,1;]' - .. pipeworks.fs_helpers.get_inv(5.5) + .. 'list[context;src;0.22,4.3;9,1;]' + .. pipeworks.fs_helpers.get_inv(6) .. 'listring[current_player;main]' .. 'listring[context;src]' .. 'listring[current_player;main]' @@ -352,8 +354,8 @@ local function update_meta(pos, meta) if output:is_empty() then -- doesn't matter if paused or not meta:set_string('infotext', S 'unconfigured Autocrafter') return false - elseif processor:is_empty() then - meta:set_string("infotext", S 'No crafting processor.') + elseif processor:is_empty() then + meta:set_string('infotext', S 'No crafting processor.') return false end @@ -369,13 +371,7 @@ local list_cache = sbz_api.make_cache('list_cache', 0, true) -- crafting processors & stats -- might want to introduce a register_crafting_processor function sometime -local processor_stats_map = { - ["sbz_resources:simple_crafting_processor"] = { crafts = 1, power = 10 }, - ["sbz_resources:quick_crafting_processor"] = { crafts = 2, power = 25 }, - ["sbz_resources:fast_crafting_processor"] = { crafts = 4, power = 50 }, - ["sbz_resources:accelerated_silicon_crafting_processor"] = { crafts = 8, power = 100 }, - ["sbz_resources:nuclear_crafting_processor"] = { crafts = 16, power = 175 }, -} +-- WARN: sbz_api.crafting_processor_stats moved to sbz_resources/processors_and_circuits.lua and this depends on them minetest.register_node('pipeworks:autocrafter', { description = S 'Autocrafter', @@ -428,6 +424,7 @@ minetest.register_node('pipeworks:autocrafter', { if that_stack:get_name() == stackname or that_stack:get_name() == '' then leftover = math.max(0, new_count - stack_max) that_stack:set_count(new_count - leftover) + that_stack:set_name(stackname) srclist[i] = that_stack stack:set_count(leftover) stack:set_name(stackname) @@ -449,6 +446,7 @@ minetest.register_node('pipeworks:autocrafter', { for i = 1, 9 do if slots[i] == stack:get_name() then local that_stack = inv:get_stack('src', i) + if that_stack:get_name() == '' then that_stack:set_count(0) end -- FIX #189 local leftover = that_stack:add_item(stack):get_count() compare_stack:set_count(leftover) end @@ -573,7 +571,7 @@ minetest.register_node('pipeworks:autocrafter', { info_extra = 'Requires a crafting processor to work.', action = function(pos, node, meta, supply, demand) local inv = meta:get_inventory() - local processor_stack = inv:get_stack("processor", 1) + local processor_stack = inv:get_stack('processor', 1) if processor_stack:is_empty() then meta:set_string('infotext', 'No crafting processor.') @@ -581,7 +579,7 @@ minetest.register_node('pipeworks:autocrafter', { end local item_name = processor_stack:get_name() - local stats = processor_stats_map[item_name] + local stats = sbz_api.crafting_processor_stats[item_name] if not stats then meta:set_string('infotext', 'This item is not a crafting processor.') @@ -617,12 +615,10 @@ minetest.register_node('pipeworks:autocrafter', { end local usage_percent = 0 - if max_crafts > 0 then - usage_percent = math.floor((crafts_succeeded / max_crafts) * 100) - end + if max_crafts > 0 then usage_percent = math.floor((crafts_succeeded / max_crafts) * 100) end local infotext = string.format( - "Active, consuming %d power. | CPU Usage: %d%% (%d/%d)", + 'Active, consuming %d power. | CPU Usage: %d%% (%d/%d)', power_demand, usage_percent, crafts_succeeded, @@ -668,3 +664,19 @@ minetest.register_craft { { 'sbz_chem:titanium_alloy_ingot', 'sbz_meteorites:neutronium', 'sbz_chem:titanium_alloy_ingot' }, }, } + +-- legacy compatibility +core.register_lbm { + label = 'Upgrade autocrafters, and give them a free processor', + name = 'pipeworks:update_autocrafters', + nodenames = { 'pipeworks:autocrafter' }, + action = function(pos, node, dtime_s) + local meta = core.get_meta(pos) + local inv = meta:get_inventory() + local has_processor = inv:get_size 'processor' ~= 0 + if not has_processor then + inv:set_size('processor', 1) + inv:set_stack('processor', 1, 'sbz_resources:simple_crafting_processor 1') + end + end, +} diff --git a/mods/sbz_pipeworks/basic_tubes.lua b/mods/sbz_pipeworks/basic_tubes.lua index 90d886f9..06a35fa4 100644 --- a/mods/sbz_pipeworks/basic_tubes.lua +++ b/mods/sbz_pipeworks/basic_tubes.lua @@ -1,53 +1,57 @@ -pipeworks.register_tube("pipeworks:tube", { - description = "Basic Tube", - plain = { { name = "basic_tube_plain.png", backface_culling = pipeworks.tube_backface_culling } }, - noctr = { { name = "basic_tube_noctr.png", backface_culling = pipeworks.tube_backface_culling } }, +pipeworks.register_tube('pipeworks:tube', { + description = 'Basic Tube', + plain = { { name = 'basic_tube_plain.png', backface_culling = pipeworks.tube_backface_culling } }, + noctr = { { name = 'basic_tube_noctr.png', backface_culling = pipeworks.tube_backface_culling } }, }) -minetest.register_craft({ - output = "pipeworks:tube_1 8", +minetest.register_craft { + output = 'pipeworks:tube_1 8', recipe = { - { "sbz_chem:aluminum_ingot", "", "sbz_chem:aluminum_ingot" }, - { "", "", "" }, - { "sbz_chem:aluminum_ingot", "", "sbz_chem:aluminum_ingot" }, - } -}) + { 'sbz_chem:aluminum_ingot', '', 'sbz_chem:aluminum_ingot' }, + { '', '', '' }, + { 'sbz_chem:aluminum_ingot', '', 'sbz_chem:aluminum_ingot' }, + }, +} -pipeworks.register_tube("pipeworks:accelerator_tube", { - description = "Accelerating Tube", - plain = { { name = "basic_tube_plain.png", backface_culling = pipeworks.tube_backface_culling, color = "springgreen" } }, - noctr = { { name = "basic_tube_noctr.png", backface_culling = pipeworks.tube_backface_culling, color = "springgreen" } }, +pipeworks.register_tube('pipeworks:accelerator_tube', { + description = 'Accelerating Tube', + plain = { + { name = 'basic_tube_plain.png', backface_culling = pipeworks.tube_backface_culling, color = 'springgreen' }, + }, + noctr = { + { name = 'basic_tube_noctr.png', backface_culling = pipeworks.tube_backface_culling, color = 'springgreen' }, + }, node_def = { tube = { can_go = function(pos, node, velocity, stack) velocity.speed = velocity.speed + 1 return pipeworks.notvel(pipeworks.meseadjlist, velocity) - end - } + end, + }, }, }) -local ch_pa = "sbz_resources:charged_particle" -minetest.register_craft({ - output = "pipeworks:accelerator_tube_1 1", +local ch_pa = 'sbz_resources:charged_particle' +minetest.register_craft { + output = 'pipeworks:accelerator_tube_1 1', recipe = { - { ch_pa, ch_pa, ch_pa }, - { ch_pa, "pipeworks:tube_1", ch_pa }, - { ch_pa, ch_pa, ch_pa }, - } -}) + { ch_pa, ch_pa, ch_pa }, + { ch_pa, 'pipeworks:tube_1', ch_pa }, + { ch_pa, ch_pa, ch_pa }, + }, +} -pipeworks.register_tube("pipeworks:one_direction_tube", { - description = "One Direction Tube", - plain = { { name = "basic_tube_plain.png", backface_culling = pipeworks.tube_backface_culling, color = "#45283c" } }, - noctr = { { name = "basic_tube_noctr.png", backface_culling = pipeworks.tube_backface_culling, color = "#45283c" } }, +pipeworks.register_tube('pipeworks:one_direction_tube', { + description = 'One Direction Tube', + plain = { { name = 'basic_tube_plain.png', backface_culling = pipeworks.tube_backface_culling, color = '#45283c' } }, + noctr = { { name = 'basic_tube_noctr.png', backface_culling = pipeworks.tube_backface_culling, color = '#45283c' } }, node_def = { on_construct = function(pos) local likely_dir = nil iterate_around_pos(pos, function(ipos) - if core.get_node(ipos).name:find("pipeworks:one_direction_tube") then - local dir = core.get_meta(ipos):get_int("dir") + if core.get_node(ipos).name:find 'pipeworks:one_direction_tube' then + local dir = core.get_meta(ipos):get_int 'dir' likely_dir = dir end end) @@ -55,39 +59,42 @@ pipeworks.register_tube("pipeworks:one_direction_tube", { if likely_dir == nil then likely_dir = core.dir_to_wallmounted(vector.new(0, -1, 0)) end local meta = core.get_meta(pos) - meta:set_int("dir", likely_dir) + meta:set_int('dir', likely_dir) end, -- gets repeated every half second sbz_on_hover = function(pointed_thing, player) local pos = pointed_thing.under local meta = core.get_meta(pos) - local dir = core.wallmounted_to_dir(meta:get_int("dir")) + local dir = core.wallmounted_to_dir(meta:get_int 'dir') - core.add_particle({ - pos = vector.add( - pointed_thing.under, - vector.divide(dir, 2)), + core.add_particle { + pos = vector.add(pointed_thing.under, vector.divide(dir, 2)), expirationtime = 1, size = 4, - texture = "star.png", + texture = 'star.png', playername = player:get_player_name(), glow = 2, - velocity = vector.divide(dir, 5) - }) + velocity = vector.divide(dir, 5), + } end, on_punch = function(pos, node, puncher, pointed_thing) - if puncher and not puncher.is_fake_player and puncher:is_player() and not core.is_protected(pos, puncher:get_player_name()) then + if + puncher + and not puncher.is_fake_player + and puncher:is_player() + and not core.is_protected(pos, puncher:get_player_name()) + then local controls = puncher:get_player_control() if controls.sneak then local face = vector.subtract(pointed_thing.above, pointed_thing.under) local dir = core.dir_to_wallmounted(face) - core.get_meta(pos):set_int("dir", dir) + core.get_meta(pos):set_int('dir', dir) end end end, tube = { can_go = function(pos, node, velocity, stack) - local dir = core.wallmounted_to_dir(core.get_meta(pos):get_int("dir")) + local dir = core.wallmounted_to_dir(core.get_meta(pos):get_int 'dir') return { dir } end, --[[ @@ -96,71 +103,83 @@ pipeworks.register_tube("pipeworks:one_direction_tube", { return vector.equals(dir, direction) end, ]] - } + }, }, }) -minetest.register_craft({ - output = "pipeworks:one_direction_tube_1", - type = "shapeless", - recipe = { "pipeworks:one_way_tube", "pipeworks:tube_1" } -}) +minetest.register_craft { + output = 'pipeworks:one_direction_tube_1', + type = 'shapeless', + recipe = { 'pipeworks:one_way_tube', 'pipeworks:tube_1' }, +} -pipeworks.register_tube("pipeworks:high_priority_tube", { - description = "High Priority Tube", - plain = { { name = "basic_tube_plain.png", backface_culling = pipeworks.tube_backface_culling, color = "tomato" } }, - noctr = { { name = "basic_tube_noctr.png", backface_culling = pipeworks.tube_backface_culling, color = "tomato" } }, +pipeworks.register_tube('pipeworks:high_priority_tube', { + description = 'High Priority Tube', + plain = { { name = 'basic_tube_plain.png', backface_culling = pipeworks.tube_backface_culling, color = 'tomato' } }, + noctr = { { name = 'basic_tube_noctr.png', backface_culling = pipeworks.tube_backface_culling, color = 'tomato' } }, node_def = { tube = { priority = 150, - } - } + }, + }, }) -minetest.register_craft({ - output = "pipeworks:high_priority_tube_1 1", - type = "shapeless", - recipe = { "pipeworks:tube_1", "sbz_resources:matter_dust" } -}) +minetest.register_craft { + output = 'pipeworks:high_priority_tube_1 1', + type = 'shapeless', + recipe = { 'pipeworks:tube_1', 'sbz_resources:matter_dust' }, +} -pipeworks.register_tube("pipeworks:low_priority_tube", { - description = "Low Priority Tube", - plain = { { name = "basic_tube_plain.png", backface_culling = pipeworks.tube_backface_culling, color = "lightgreen" } }, - noctr = { { name = "basic_tube_noctr.png", backface_culling = pipeworks.tube_backface_culling, color = "lightgreen" } }, +pipeworks.register_tube('pipeworks:low_priority_tube', { + description = 'Low Priority Tube', + plain = { + { name = 'basic_tube_plain.png', backface_culling = pipeworks.tube_backface_culling, color = 'lightgreen' }, + }, + noctr = { + { name = 'basic_tube_noctr.png', backface_culling = pipeworks.tube_backface_culling, color = 'lightgreen' }, + }, node_def = { tube = { priority = 1, - } - } -}) - -minetest.register_craft({ - output = "pipeworks:low_priority_tube_1 1", - type = "shapeless", - recipe = { "pipeworks:tube_1", "sbz_resources:antimatter_dust" } + }, + }, }) +minetest.register_craft { + output = 'pipeworks:low_priority_tube_1 1', + type = 'shapeless', + recipe = { 'pipeworks:tube_1', 'sbz_resources:antimatter_dust' }, +} -minetest.register_node("pipeworks:one_way_tube", { - description = "One-Way Tube", +minetest.register_node('pipeworks:one_way_tube', { + description = 'One-Way Tube', tiles = { - { name = "one_way_tube_top.png", backface_culling = pipeworks.tube_backface_culling }, - { name = "one_way_tube_top.png", backface_culling = pipeworks.tube_backface_culling }, - { name = "basic_tube_plain.png", backface_culling = pipeworks.tube_backface_culling }, - { name = "basic_tube_plain.png", backface_culling = pipeworks.tube_backface_culling }, - { name = "one_way_tube_top.png^[transformFX", backface_culling = pipeworks.tube_backface_culling }, - { name = "one_way_tube_top.png", backface_culling = pipeworks.tube_backface_culling }, + { name = 'one_way_tube_top.png', backface_culling = pipeworks.tube_backface_culling }, + { name = 'one_way_tube_top.png', backface_culling = pipeworks.tube_backface_culling }, + { name = 'basic_tube_plain.png', backface_culling = pipeworks.tube_backface_culling }, + { name = 'basic_tube_plain.png', backface_culling = pipeworks.tube_backface_culling }, + { name = 'one_way_tube_top.png^[transformFX', backface_culling = pipeworks.tube_backface_culling }, + { name = 'one_way_tube_top.png', backface_culling = pipeworks.tube_backface_culling }, }, - use_texture_alpha = "clip", - paramtype2 = "facedir", - drawtype = "nodebox", - paramtype = "light", + use_texture_alpha = 'clip', + paramtype2 = 'facedir', + drawtype = 'nodebox', + paramtype = 'light', node_box = { - type = "fixed", - fixed = pipeworks.tube_long + type = 'fixed', + fixed = pipeworks.tube_long, + }, + groups = { + matter = 2, + snappy = 2, + choppy = 2, + oddly_breakable_by_hand = 2, + tubedevice = 1, + axey = 1, + handy = 1, + pickaxey = 1, }, - groups = { matter = 2, snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, tubedevice = 1, axey = 1, handy = 1, pickaxey = 1 }, is_ground_content = false, tube = { connect_sides = { left = 1, right = 1 }, @@ -171,52 +190,60 @@ minetest.register_node("pipeworks:one_way_tube", { local dir = pipeworks.facedir_to_right_dir(node.param2) return vector.equals(dir, direction) end, - priority = 75 -- Higher than normal tubes, but lower than receivers + priority = 75, -- Higher than normal tubes, but lower than receivers }, after_place_node = pipeworks.after_place, after_dig_node = pipeworks.after_dig, on_rotate = pipeworks.on_rotate, }) -minetest.register_craft({ - output = "pipeworks:one_way_tube", +minetest.register_craft { + output = 'pipeworks:one_way_tube', recipe = { - { "", "sbz_resources:matter_dust", "" }, - { "", "pipeworks:tube_1", "" }, - { "", "sbz_resources:matter_dust", "" } - } -}) + { '', 'sbz_resources:matter_dust', '' }, + { '', 'pipeworks:tube_1', '' }, + { '', 'sbz_resources:matter_dust', '' }, + }, +} -pipeworks.register_tube("pipeworks:crossing_tube", { - description = "Crossing tube", - plain = { "crossing_tube_plain.png" }, - noctr = { "crossing_tube_noctr.png" }, +pipeworks.register_tube('pipeworks:crossing_tube', { + description = 'Crossing tube', + plain = { 'crossing_tube_plain.png' }, + noctr = { 'crossing_tube_noctr.png' }, node_def = { - tube = { can_go = function(pos, node, velocity, stack) return { velocity } end } + tube = { + can_go = function(pos, node, velocity, stack) + return { velocity } + end, + }, }, }) -minetest.register_craft({ - output = "pipeworks:crossing_tube_1 5", +minetest.register_craft { + output = 'pipeworks:crossing_tube_1 5', recipe = { - { "", "pipeworks:tube_1", "" }, - { "pipeworks:tube_1", "pipeworks:tube_1", "pipeworks:tube_1" }, - { "", "pipeworks:tube_1", "" } - } -}) + { '', 'pipeworks:tube_1', '' }, + { 'pipeworks:tube_1', 'pipeworks:tube_1', 'pipeworks:tube_1' }, + { '', 'pipeworks:tube_1', '' }, + }, +} -pipeworks.register_tube("pipeworks:broken_tube", { - description = "Broken Tube", - plain = { { name = "pipeworks_broken_tube_plain.png", backface_culling = pipeworks.tube_backface_culling, color = "red" } }, - noctr = { { name = "pipeworks_broken_tube_plain.png", backface_culling = pipeworks.tube_backface_culling, color = "red" } }, +pipeworks.register_tube('pipeworks:broken_tube', { + description = 'Broken Tube', + plain = { + { name = 'pipeworks_broken_tube_plain.png', backface_culling = pipeworks.tube_backface_culling, color = 'red' }, + }, + noctr = { + { name = 'pipeworks_broken_tube_plain.png', backface_culling = pipeworks.tube_backface_culling, color = 'red' }, + }, node_def = { - drop = "pipeworks:tube_1", + drop = 'pipeworks:tube_1', groups = { not_in_creative_inventory = 1, tubedevice_receiver = 1 }, is_ground_content = false, tube = { insert_object = function(pos, node, stack, direction) minetest.item_drop(stack, nil, pos) - return ItemStack("") + return ItemStack '' end, can_insert = function(pos, node, stack, direction) return true @@ -227,40 +254,38 @@ pipeworks.register_tube("pipeworks:broken_tube", { local itemstack = puncher:get_wielded_item() local wieldname = itemstack:get_name() local playername = puncher:get_player_name() - local log_msg = playername .. " struck a broken tube at " .. minetest.pos_to_string(pos) .. "\n " + local log_msg = playername .. ' struck a broken tube at ' .. minetest.pos_to_string(pos) .. '\n ' local meta = minetest.get_meta(pos) - local was_node = minetest.deserialize(meta:get_string("the_tube_was")) + local was_node = minetest.deserialize(meta:get_string 'the_tube_was') if not was_node then pipeworks.logger(log_msg .. "but it can't be repaired.") return end if not pipeworks.check_and_wear_hammer(puncher) then - if wieldname == "" then - minetest.chat_send_player(playername, - ("Broken tubes may be a bit sharp. Maybe try hitting it with a robotic arm?")) - if minetest.settings:get_bool("enable_damage") then - puncher:set_hp(puncher:get_hp() - 1) - end + if wieldname == '' then + minetest.chat_send_player( + playername, + 'Broken tubes may be a bit sharp. Maybe try hitting it with a robotic arm?' + ) + if minetest.settings:get_bool 'enable_damage' then puncher:set_hp(puncher:get_hp() - 1) end end return end - log_msg = log_msg .. "with " .. wieldname .. " to repair it" + log_msg = log_msg .. 'with ' .. wieldname .. ' to repair it' local nodedef = minetest.registered_nodes[was_node.name] if nodedef then - pipeworks.logger(log_msg .. ".") + pipeworks.logger(log_msg .. '.') if nodedef.tube and nodedef.tube.on_repair then nodedef.tube.on_repair(pos, was_node) else minetest.swap_node(pos, { name = was_node.name, param2 = was_node.param2 }) pipeworks.scan_for_tube_objects(pos) end - if meta:get_string("infotext") == "Broken Tube" then - meta:set_string("infotext", "") - end - meta:set_string("the_tube_was", "") + if meta:get_string 'infotext' == 'Broken Tube' then meta:set_string('infotext', '') end + meta:set_string('the_tube_was', '') else - pipeworks.logger(log_msg .. " but original node " .. was_node.name .. " is not registered anymore.") - minetest.chat_send_player(playername, ("This tube cannot be repaired.")) + pipeworks.logger(log_msg .. ' but original node ' .. was_node.name .. ' is not registered anymore.') + minetest.chat_send_player(playername, 'This tube cannot be repaired.') end end, allow_metadata_inventory_put = function() @@ -272,5 +297,5 @@ pipeworks.register_tube("pipeworks:broken_tube", { allow_metadata_inventory_take = function() return 0 end, - } + }, }) diff --git a/mods/sbz_pipeworks/filter_injector.lua b/mods/sbz_pipeworks/filter_injector.lua index be4de258..e98247b0 100644 --- a/mods/sbz_pipeworks/filter_injector.lua +++ b/mods/sbz_pipeworks/filter_injector.lua @@ -42,7 +42,7 @@ local animation_def = { minetest.register_node('pipeworks:automatic_filter_injector', { description = 'Automatic Filter-Injector', - info_extra = 'Pushes items out of blocks... and into tubes or other blocks', + info_extra = 'Pushes items out of containers.', tiles = { { name = 'filter_side.png^[transformFX', animation = animation_def }, { name = 'filter_side.png^[transformFX', animation = animation_def }, @@ -117,7 +117,7 @@ minetest.register_node('pipeworks:automatic_filter_injector', { local fromnode = sbz_api.get_or_load_node(frompos) if not fromnode then - meta:set_string('infotext', "Can't pull from that node - there is no node there?") + meta:set_string('infotext', "Can't pull from that node, there is no node there???") return 1 end diff --git a/mods/sbz_pipeworks/init.lua b/mods/sbz_pipeworks/init.lua index 8105e30a..b25c65ae 100644 --- a/mods/sbz_pipeworks/init.lua +++ b/mods/sbz_pipeworks/init.lua @@ -3,34 +3,33 @@ -- This mod supplies various steel pipes and plastic pneumatic tubes -- and devices that they can connect to. - +---@class pipeworks pipeworks = { - worldpath = minetest.get_worldpath(), - modpath = minetest.get_modpath("pipeworks"), - logger = function(msg) - minetest.log("action", "[pipeworks] " .. msg) - end, - entity_update_interval = 0.1, - use_real_entities = true, - enable_cyclic_mode = true, - tube_backface_culling = core.settings:get_bool("sbz_pipe_backface_culling", true), + worldpath = minetest.get_worldpath(), + modpath = minetest.get_modpath 'pipeworks', + logger = function(msg) + minetest.log('action', '[pipeworks] ' .. msg) + end, + entity_update_interval = 0.1, + use_real_entities = true, + enable_cyclic_mode = true, + tube_backface_culling = core.settings:get_bool('sbz_pipe_backface_culling', true), } - -- Load the various other parts of the mod -dofile(pipeworks.modpath .. "/common.lua") -dofile(pipeworks.modpath .. "/models.lua") -dofile(pipeworks.modpath .. "/autoplace_tubes.lua") -dofile(pipeworks.modpath .. "/luaentity.lua") -dofile(pipeworks.modpath .. "/item_transport.lua") -dofile(pipeworks.modpath .. "/tube_register.lua") +dofile(pipeworks.modpath .. '/common.lua') +dofile(pipeworks.modpath .. '/models.lua') +dofile(pipeworks.modpath .. '/autoplace_tubes.lua') +dofile(pipeworks.modpath .. '/luaentity.lua') +dofile(pipeworks.modpath .. '/item_transport.lua') +dofile(pipeworks.modpath .. '/tube_register.lua') -dofile(pipeworks.modpath .. "/filter_injector.lua") -dofile(pipeworks.modpath .. "/basic_tubes.lua") -dofile(pipeworks.modpath .. "/basic_blocks.lua") +dofile(pipeworks.modpath .. '/filter_injector.lua') +dofile(pipeworks.modpath .. '/basic_tubes.lua') +dofile(pipeworks.modpath .. '/basic_blocks.lua') -dofile(pipeworks.modpath .. "/wielder.lua") -dofile(pipeworks.modpath .. "/autocrafter.lua") -dofile(pipeworks.modpath .. "/teleport_tube.lua") -dofile(pipeworks.modpath .. "/pattern_storinator.lua") -minetest.log("info", "Pipeworks loaded!") +dofile(pipeworks.modpath .. '/wielder.lua') +dofile(pipeworks.modpath .. '/autocrafter.lua') +dofile(pipeworks.modpath .. '/teleport_tube.lua') +dofile(pipeworks.modpath .. '/pattern_storinator.lua') +minetest.log('info', 'Pipeworks loaded!') diff --git a/mods/sbz_pipeworks/item_transport.lua b/mods/sbz_pipeworks/item_transport.lua index f40a353a..ac62c1a5 100644 --- a/mods/sbz_pipeworks/item_transport.lua +++ b/mods/sbz_pipeworks/item_transport.lua @@ -1,32 +1,32 @@ local luaentity = pipeworks.luaentity -local enable_max_limit = minetest.settings:get_bool("pipeworks_enable_items_per_tube_limit") -local max_tube_limit = tonumber(minetest.settings:get("pipeworks_max_items_per_tube")) or 30 +local enable_max_limit = minetest.settings:get_bool 'pipeworks_enable_items_per_tube_limit' +local max_tube_limit = tonumber(minetest.settings:get 'pipeworks_max_items_per_tube') or 30 if enable_max_limit == nil then enable_max_limit = true end function pipeworks.tube_inject_item(pos, start_pos, velocity, item, owner) - -- Take item in any format - local stack = ItemStack(item) - local obj = luaentity.add_entity(pos, "pipeworks:tubed_item") - obj:set_item(stack:to_string()) - obj.start_pos = vector.copy(start_pos) - obj:set_velocity(velocity) - obj.owner = owner - obj.tags = {} - return obj + -- Take item in any format + local stack = ItemStack(item) + local obj = luaentity.add_entity(pos, 'pipeworks:tubed_item') + obj:set_item(stack:to_string()) + obj.start_pos = vector.copy(start_pos) + obj:set_velocity(velocity) + obj.owner = owner + obj.tags = {} + return obj end -function pipeworks.tube_inject_direct(pos, start_pos, to_pos, velocity, item, owner) -- function needed because monitoring pipeworks.tube_inject_item - local node = sbz_api.get_or_load_node(to_pos) - local stack = ItemStack(item) - if node and core.registered_nodes[node.name] then - local def = core.registered_nodes[node.name] - if def.tube and def.tube.insert_object then - local leftover = def.tube.insert_object(to_pos, node, stack, velocity, owner) - stack = leftover - if stack:is_empty() then return false end - end - end - return pipeworks.tube_inject_item(pos, start_pos, velocity, stack, owner) -- leftover +function pipeworks.tube_inject_direct(pos, start_pos, to_pos, velocity, item, owner) -- function needed because monitoring monitors pipeworks.tube_inject_item + local node = sbz_api.get_or_load_node(to_pos) + local stack = ItemStack(item) + if node and core.registered_nodes[node.name] then + local def = core.registered_nodes[node.name] + if def.tube and def.tube.insert_object then + local leftover = def.tube.insert_object(to_pos, node, stack, velocity, owner) + stack = leftover + if stack:is_empty() then return false end + end + end + return pipeworks.tube_inject_item(pos, start_pos, velocity, stack, owner) -- leftover end -- adding two tube functions @@ -34,156 +34,150 @@ end -- remove_items(pos,node,stack,dir,count) removes count items and returns them -- both optional w/ sensible defaults and fallback to normal allow_* function -local default_adjlist = { { x = 0, y = 0, z = 1 }, { x = 0, y = 0, z = -1 }, { x = 0, y = 1, z = 0 }, { x = 0, y = -1, z = 0 }, { x = 1, y = 0, z = 0 }, { x = -1, y = 0, z = 0 } } +local default_adjlist = { + { x = 0, y = 0, z = 1 }, + { x = 0, y = 0, z = -1 }, + { x = 0, y = 1, z = 0 }, + { x = 0, y = -1, z = 0 }, + { x = 1, y = 0, z = 0 }, + { x = -1, y = 0, z = 0 }, +} function pipeworks.notvel(tbl, vel) - local tbl2 = {} - for _, val in ipairs(tbl) do - if val.x ~= -vel.x or val.y ~= -vel.y or val.z ~= -vel.z then table.insert(tbl2, val) end - end - return tbl2 + local tbl2 = {} + for _, val in ipairs(tbl) do + if val.x ~= -vel.x or val.y ~= -vel.y or val.z ~= -vel.z then table.insert(tbl2, val) end + end + return tbl2 end local tube_item_count = {} minetest.register_globalstep(function(dtime) - if not luaentity.entities then - return - end - tube_item_count = {} - - -- start removing entities after there are too much - local too_many_entities = 1000 - - local count = 0 - for _, entity in pairs(luaentity.entities) do - if entity.name == "pipeworks:tubed_item" then - local h = minetest.hash_node_position(vector.round(entity._pos)) - if h ~= nil and not minetest.is_nan(h) then - count = count + 1 - tube_item_count[h] = (tube_item_count[h] or 0) + 1 - else - entity:remove() - end - end - end - if count > too_many_entities then - local to_remove = (count - too_many_entities) - local removed = 0 - for _, entity in pairs(luaentity.entities) do - if entity.name == "pipeworks:tubed_item" then - entity:remove() - end - removed = removed + 1 - if removed >= to_remove then - break - end - end - end + if not luaentity.entities then return end + tube_item_count = {} + + -- start removing entities after there are too much + local too_many_entities = 1000 + + local count = 0 + for _, entity in pairs(luaentity.entities) do + if entity.name == 'pipeworks:tubed_item' then + local h = minetest.hash_node_position(vector.round(entity._pos)) + if h ~= nil and not minetest.is_nan(h) then + count = count + 1 + tube_item_count[h] = (tube_item_count[h] or 0) + 1 + else + entity:remove() + end + end + end + if count > too_many_entities then + local to_remove = (count - too_many_entities) + local removed = 0 + for _, entity in pairs(luaentity.entities) do + if entity.name == 'pipeworks:tubed_item' then entity:remove() end + removed = removed + 1 + if removed >= to_remove then break end + end + end end) - - -- tube overload mechanism: -- when the tube's item count (tracked in the above tube_item_count table) -- exceeds the limit configured per tube, replace it with a broken one. function pipeworks.break_tube(pos) - local node = minetest.get_node(pos) - local meta = minetest.get_meta(pos) - meta:set_string("the_tube_was", minetest.serialize(node)) - minetest.swap_node(pos, { name = "pipeworks:broken_tube_1" }) - core.get_meta(pos):set_string("infotext", "Broken Tube") - pipeworks.scan_for_tube_objects(pos) + local node = minetest.get_node(pos) + local meta = minetest.get_meta(pos) + meta:set_string('the_tube_was', minetest.serialize(node)) + minetest.swap_node(pos, { name = 'pipeworks:broken_tube_1' }) + core.get_meta(pos):set_string('infotext', 'Broken Tube') + pipeworks.scan_for_tube_objects(pos) end local crunch_tube = function(pos, cnode, cmeta) - if enable_max_limit then - local h = minetest.hash_node_position(pos) - local itemcount = tube_item_count[h] or 0 - if itemcount > max_tube_limit then - pipeworks.logger("Warning - a tube at " .. - minetest.pos_to_string(pos) .. " broke due to too many items (" .. itemcount .. ")") - pipeworks.break_tube(pos) - end - end + if enable_max_limit then + local h = minetest.hash_node_position(pos) + local itemcount = tube_item_count[h] or 0 + if itemcount > max_tube_limit then + pipeworks.logger( + 'Warning - a tube at ' + .. minetest.pos_to_string(pos) + .. ' broke due to too many items (' + .. itemcount + .. ')' + ) + pipeworks.break_tube(pos) + end + end end - - local adjlist_cache = {} -- compatibility behaviour for the existing can_go() callbacks, -- which can only specify a list of possible positions. -- the fact that minetest.deserialize used to be in this function just not cached hurts my soul local function go_next_compat(pos, cnode, cmeta, cycledir, vel, stack, owner, tags) - local next_positions = {} - local max_priority = 0 - local can_go - - local def = minetest.registered_nodes[cnode.name] - if def and def.tube and def.tube.can_go then - can_go = def.tube.can_go(pos, cnode, vel, stack, tags) - else - local adjlist_string = minetest.get_meta(pos):get_string("adjlist") - local adjlist - if adjlist_string ~= "" then - adjlist = adjlist_cache[adjlist_string] - if not adjlist then - adjlist_cache[adjlist_string] = core.deserialize(adjlist_string) - adjlist = adjlist_cache[adjlist_string] - end - else - adjlist = default_adjlist - end - if not adjlist then - adjlist = default_adjlist - end - - can_go = pipeworks.notvel(adjlist, vel) - end - -- can_go() is expected to return an array-like table of candidate offsets. - -- for each one, look at the node at that offset and determine if it can accept the item. - -- also note the prioritisation: - -- if any tube is found with a greater priority than previously discovered, - -- then the valid positions are reset and and subsequent positions under this are skipped. - -- this has the effect of allowing only equal priorities to co-exist. - for _, vect in ipairs(can_go) do - local npos = vector.add(pos, vect) - pipeworks.load_position(npos) - local node = minetest.get_node(npos) - local reg_node = minetest.registered_nodes[node.name] - if reg_node then - local tube_def = reg_node.tube - local tubedevice = minetest.get_item_group(node.name, "tubedevice") - local tube_priority = (tube_def and tube_def.priority) or 100 - if tubedevice > 0 and tube_priority >= max_priority then - if not tube_def or not tube_def.can_insert or - tube_def.can_insert(npos, node, stack, vect, owner) then - if tube_priority > max_priority then - max_priority = tube_priority - next_positions = {} - end - next_positions[#next_positions + 1] = { pos = npos, vect = vect } - end - end - end - end - - -- indicate not found if no valid rules were picked up, - -- and don't change the counter. - if not next_positions[1] then - return cycledir, false, nil, nil - end - - -- otherwise rotate to the next output direction and return that - local n = (cycledir % (#next_positions)) + 1 - local new_velocity = vector.multiply(next_positions[n].vect, vel.speed) - return n, true, new_velocity, nil + local next_positions = {} + local max_priority = 0 + local can_go + + local def = minetest.registered_nodes[cnode.name] + if def and def.tube and def.tube.can_go then + can_go = def.tube.can_go(pos, cnode, vel, stack, tags) + else + local adjlist_string = minetest.get_meta(pos):get_string 'adjlist' + local adjlist + if adjlist_string ~= '' then + adjlist = adjlist_cache[adjlist_string] + if not adjlist then + adjlist_cache[adjlist_string] = core.deserialize(adjlist_string) + adjlist = adjlist_cache[adjlist_string] + end + else + adjlist = default_adjlist + end + if not adjlist then adjlist = default_adjlist end + + can_go = pipeworks.notvel(adjlist, vel) + end + -- can_go() is expected to return an array-like table of candidate offsets. + -- for each one, look at the node at that offset and determine if it can accept the item. + -- also note the prioritisation: + -- if any tube is found with a greater priority than previously discovered, + -- then the valid positions are reset and and subsequent positions under this are skipped. + -- this has the effect of allowing only equal priorities to co-exist. + for _, vect in ipairs(can_go) do + local npos = vector.add(pos, vect) + pipeworks.load_position(npos) + local node = minetest.get_node(npos) + local reg_node = minetest.registered_nodes[node.name] + if reg_node then + local tube_def = reg_node.tube + local tubedevice = minetest.get_item_group(node.name, 'tubedevice') + local tube_priority = (tube_def and tube_def.priority) or 100 + if tubedevice > 0 and tube_priority >= max_priority then + if not tube_def or not tube_def.can_insert or tube_def.can_insert(npos, node, stack, vect, owner) then + if tube_priority > max_priority then + max_priority = tube_priority + next_positions = {} + end + next_positions[#next_positions + 1] = { pos = npos, vect = vect } + end + end + end + end + + -- indicate not found if no valid rules were picked up, + -- and don't change the counter. + if not next_positions[1] then return cycledir, false, nil, nil end + + -- otherwise rotate to the next output direction and return that + local n = (cycledir % #next_positions) + 1 + local new_velocity = vector.multiply(next_positions[n].vect, vel.speed) + return n, true, new_velocity, nil end - - - -- function called by the on_step callback of the pipeworks tube luaentity. -- the routine is passed the current node position, velocity, itemstack, -- and owner name. @@ -194,64 +188,56 @@ end -- if this is not nil, the luaentity spawns new tubed items for each new fragment stack, -- then deletes itself (i.e. the original item stack). local function go_next(pos, velocity, stack, owner, tags) - local cnode = minetest.get_node(pos) - if cnode.name == "ignore" then - return false, velocity, nil - end - local cmeta = minetest.get_meta(pos) - local speed = math.abs(velocity.x + velocity.y + velocity.z) - if speed == 0 then - speed = 1 - end - local vel = { x = velocity.x / speed, y = velocity.y / speed, z = velocity.z / speed, speed = speed } - if speed >= 4.1 then - speed = 4 - elseif speed >= 1.1 then - speed = speed - 0.1 - else - speed = 1 - end - vel.speed = speed - - crunch_tube(pos, cnode, cmeta) - -- cycling of outputs: - -- an integer counter is kept in each pipe's metadata, - -- which allows tracking which output was previously chosen. - -- note reliance on get_int returning 0 for uninitialised. - local cycledir = cmeta:get_int("tubedir") - - -- pulled out and factored out into go_next_compat() above. - -- n is the new value of the cycle counter. - local n, found, new_velocity, multimode = go_next_compat(pos, cnode, cmeta, cycledir, vel, stack, owner, tags) - - -- if not using output cycling, - -- don't update the field so it stays the same for the next item. - if pipeworks.enable_cyclic_mode then - cmeta:set_int("tubedir", n) - end - return found, new_velocity, multimode + local cnode = minetest.get_node(pos) + if cnode.name == 'ignore' then return false, velocity, nil end + local cmeta = minetest.get_meta(pos) + local speed = math.abs(velocity.x + velocity.y + velocity.z) + if speed == 0 then speed = 1 end + local vel = { x = velocity.x / speed, y = velocity.y / speed, z = velocity.z / speed, speed = speed } + if speed >= 4.1 then + speed = 4 + elseif speed >= 1.1 then + speed = speed - 0.1 + else + speed = 1 + end + vel.speed = speed + + crunch_tube(pos, cnode, cmeta) + -- cycling of outputs: + -- an integer counter is kept in each pipe's metadata, + -- which allows tracking which output was previously chosen. + -- note reliance on get_int returning 0 for uninitialised. + local cycledir = cmeta:get_int 'tubedir' + + -- pulled out and factored out into go_next_compat() above. + -- n is the new value of the cycle counter. + local n, found, new_velocity, multimode = go_next_compat(pos, cnode, cmeta, cycledir, vel, stack, owner, tags) + + -- if not using output cycling, + -- don't update the field so it stays the same for the next item. + if pipeworks.enable_cyclic_mode then cmeta:set_int('tubedir', n) end + return found, new_velocity, multimode end - - -minetest.register_entity("pipeworks:tubed_item", { - initial_properties = { - hp_max = 1, - physical = false, - collisionbox = { 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 }, - visual = "wielditem", - visual_size = { x = pipeworks.tube_size, y = pipeworks.tube_size }, - textures = { "" }, - spritediv = { x = 1, y = 1 }, - initial_sprite_basepos = { x = 0, y = 0 }, - is_visible = false, - }, - - physical_state = false, - - from_data = function(self, itemstring) - local stack = ItemStack(itemstring) - --[[ +minetest.register_entity('pipeworks:tubed_item', { + initial_properties = { + hp_max = 1, + physical = false, + collisionbox = { 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 }, + visual = 'wielditem', + visual_size = { x = pipeworks.tube_size, y = pipeworks.tube_size }, + textures = { '' }, + spritediv = { x = 1, y = 1 }, + initial_sprite_basepos = { x = 0, y = 0 }, + is_visible = false, + }, + + physical_state = false, + + from_data = function(self, itemstring) + local stack = ItemStack(itemstring) + --[[ local itemtable = stack:to_table() local itemname = nil if itemtable then @@ -264,231 +250,218 @@ minetest.register_entity("pipeworks:tubed_item", { item_type = minetest.registered_items[itemname].type end --]] - self.object:set_properties({ - is_visible = true, - textures = { stack:get_name() } - }) - - local def = stack:get_definition() - self.object:set_yaw((def and def.type == "node") and 0 or math.pi * 0.25) - end, - - get_staticdata = luaentity.get_staticdata, - on_activate = function(self, staticdata) -- Legacy code, should be replaced later by luaentity.on_activate - if staticdata == "" or staticdata == nil then - return - end - if staticdata == "toremove" then - self.object:remove() - return - end - local item = minetest.deserialize(staticdata) - pipeworks.tube_inject_item(self.object:get_pos(), item.start_pos, item.velocity, item.itemstring) - self.object:remove() - end, + self.object:set_properties { + is_visible = true, + textures = { stack:get_name() }, + } + + local def = stack:get_definition() + self.object:set_yaw((def and def.type == 'node') and 0 or math.pi * 0.25) + end, + + get_staticdata = luaentity.get_staticdata, + on_activate = function(self, staticdata) -- Legacy code, should be replaced later by luaentity.on_activate + if staticdata == '' or staticdata == nil then return end + if staticdata == 'toremove' then + self.object:remove() + return + end + local item = minetest.deserialize(staticdata) + pipeworks.tube_inject_item(self.object:get_pos(), item.start_pos, item.velocity, item.itemstring) + self.object:remove() + end, }) -minetest.register_entity("pipeworks:color_entity", { - initial_properties = { - hp_max = 1, - physical = false, - collisionbox = { 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 }, - visual = "cube", - visual_size = { x = 3.5, y = 3.5, z = 3.5 }, -- todo: find correct size - textures = { "" }, - is_visible = false, - }, - - physical_state = false, - - from_data = function(self, color) - local t = "pipeworks_color_" .. color .. ".png" - local prop = { - is_visible = true, - visual = "cube", - textures = { t, t, t, t, t, t } -- todo: textures - } - self.object:set_properties(prop) - end, - - get_staticdata = luaentity.get_staticdata, - on_activate = luaentity.on_activate, +minetest.register_entity('pipeworks:color_entity', { + initial_properties = { + hp_max = 1, + physical = false, + collisionbox = { 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 }, + visual = 'cube', + visual_size = { x = 3.5, y = 3.5, z = 3.5 }, -- todo: find correct size + textures = { '' }, + is_visible = false, + }, + + physical_state = false, + + from_data = function(self, color) + local t = 'pipeworks_color_' .. color .. '.png' + local prop = { + is_visible = true, + visual = 'cube', + textures = { t, t, t, t, t, t }, -- todo: textures + } + self.object:set_properties(prop) + end, + + get_staticdata = luaentity.get_staticdata, + on_activate = luaentity.on_activate, }) -- see below for usage: -- determine if go_next returned a multi-mode set. local is_multimode = function(v) - return (type(v) == "table") and (v.__multimode) + return (type(v) == 'table') and v.__multimode end -luaentity.register_entity("pipeworks:tubed_item", { - itemstring = '', - item_entity = nil, - color_entity = nil, - color = nil, - start_pos = nil, - tags = nil, - - set_item = function(self, item) - local itemstring = ItemStack(item):to_string() -- Accept any input format - if self.itemstring == itemstring then - return - end - if self.item_entity then - self:remove_attached_entity(self.item_entity) - end - self.itemstring = itemstring - self.item_entity = self:add_attached_entity("pipeworks:tubed_item", itemstring) - end, - - set_color = function(self, color) - if self.color == color then - return - end - self.color = color - if self.color_entity then - self:remove_attached_entity(self.color_entity) - end - if color then - self.color_entity = self:add_attached_entity("pipeworks:color_entity", color) - else - self.color_entity = nil - end - end, - - on_step = function(self, dtime) - local pos = self:get_pos() - if self.start_pos == nil then - self.start_pos = vector.round(pos) - self:set_pos(pos) - end - - local velocity = self:get_velocity() - - local moved = false - local speed = math.abs(velocity.x + velocity.y + velocity.z) - if speed == 0 then - speed = 1 - moved = true - end - local vel = { x = velocity.x / speed, y = velocity.y / speed, z = velocity.z / speed, speed = speed } - local moved_by = vector.distance(pos, self.start_pos) - - if moved_by >= 1 then - self.start_pos = vector.add(self.start_pos, vel) - moved = true - end - - if not moved then - return - end - - local stack = ItemStack(self.itemstring) - - pipeworks.load_position(self.start_pos) - local node = minetest.get_node(self.start_pos) - if minetest.get_item_group(node.name, "tubedevice_receiver") == 1 then - local oldstack = stack - local leftover - local def = minetest.registered_nodes[node.name] - if def.tube and def.tube.insert_object then - leftover = def.tube.insert_object(self.start_pos, node, stack, vel, self.owner) - else - leftover = stack - end - if leftover:is_empty() then - self:remove() - return - end - velocity = vector.multiply(velocity, -1) - -- this is to prevent massive amounts of tubed entities with wielders, added in sbz - local to_return = vector.add(self.start_pos, velocity) - local to_return_node = core.get_node(to_return) - local to_return_def = core.registered_nodes[to_return_node.name] - if to_return_def.tube and to_return_def.tube.can_insert and oldstack:equals(leftover) then - local can_insert = to_return_def.tube.can_insert(to_return, to_return_node, stack, velocity, self.owner) - if not can_insert then -- drop itself - local dropped_item = minetest.add_item(self.start_pos, stack) - if dropped_item then - dropped_item:set_velocity(vector.multiply(velocity, 2)) - self:remove() - end - return - end - end - - self:set_pos(vector.subtract(self.start_pos, vector.multiply(vel, moved_by - 1))) - self:set_velocity(velocity) - self:set_item(leftover:to_string()) - return - end - - local tags - if pipeworks.enable_item_tags then - tags = self.tags or {} - end - local found_next, new_velocity, multimode = go_next(self.start_pos, velocity, stack, self.owner, tags) -- todo: color - if pipeworks.enable_item_tags then - self.tags = #tags > 0 and tags or nil - end - local rev_vel = vector.multiply(velocity, -1) - local rev_dir = vector.direction(self.start_pos, vector.add(self.start_pos, rev_vel)) - local rev_node = minetest.get_node(vector.round(vector.add(self.start_pos, rev_dir))) - local tube_present = minetest.get_item_group(rev_node.name, "tubedevice") == 1 - if not found_next then - if pipeworks.drop_on_routing_fail or not tube_present or - minetest.get_item_group(rev_node.name, "tube") ~= 1 then - -- Using add_item instead of item_drop since this makes pipeworks backward - -- compatible with Minetest 0.4.13. - -- Using item_drop here makes Minetest 0.4.13 crash. - local dropped_item = minetest.add_item(self.start_pos, stack) - if dropped_item then - dropped_item:set_velocity(vector.multiply(velocity, 5)) - self:remove() - end - return - else - velocity = vector.multiply(velocity, -1) - self:set_pos(vector.subtract(self.start_pos, vector.multiply(vel, moved_by - 1))) - self:set_velocity(velocity) - end - elseif is_multimode(multimode) then - -- create new stacks according to returned data. - local s = self.start_pos - for _, split in ipairs(multimode) do - pipeworks.tube_inject_item(s, s, split.velocity, split.itemstack, self.owner) - end - -- remove ourself now the splits are sent - self:remove() - return - end - - if new_velocity and not vector.equals(velocity, new_velocity) then - local nvelr = math.abs(new_velocity.x + new_velocity.y + new_velocity.z) - self:set_pos(vector.add(self.start_pos, vector.multiply(new_velocity, (moved_by - 1) / nvelr))) - self:set_velocity(new_velocity) - end - end +luaentity.register_entity('pipeworks:tubed_item', { + itemstring = '', + item_entity = nil, + color_entity = nil, + color = nil, + start_pos = nil, + tags = nil, + + set_item = function(self, item) + local itemstring = ItemStack(item):to_string() -- Accept any input format + if self.itemstring == itemstring then return end + if self.item_entity then self:remove_attached_entity(self.item_entity) end + self.itemstring = itemstring + self.item_entity = self:add_attached_entity('pipeworks:tubed_item', itemstring) + end, + + set_color = function(self, color) + if self.color == color then return end + self.color = color + if self.color_entity then self:remove_attached_entity(self.color_entity) end + if color then + self.color_entity = self:add_attached_entity('pipeworks:color_entity', color) + else + self.color_entity = nil + end + end, + + on_step = function(self, dtime) + local pos = self:get_pos() + if self.start_pos == nil then + self.start_pos = vector.round(pos) + self:set_pos(pos) + end + + local velocity = self:get_velocity() + + local moved = false + local speed = math.abs(velocity.x + velocity.y + velocity.z) + if speed == 0 then + speed = 1 + moved = true + end + local vel = { x = velocity.x / speed, y = velocity.y / speed, z = velocity.z / speed, speed = speed } + local moved_by = vector.distance(pos, self.start_pos) + + if moved_by >= 1 then + self.start_pos = vector.add(self.start_pos, vel) + moved = true + end + + if not moved then return end + + local stack = ItemStack(self.itemstring) + + pipeworks.load_position(self.start_pos) + local node = minetest.get_node(self.start_pos) + if minetest.get_item_group(node.name, 'tubedevice_receiver') == 1 then + local oldstack = stack + local leftover + local def = minetest.registered_nodes[node.name] + if def.tube and def.tube.insert_object then + leftover = def.tube.insert_object(self.start_pos, node, stack, vel, self.owner) + else + leftover = stack + end + if leftover:is_empty() then + self:remove() + return + end + velocity = vector.multiply(velocity, -1) + -- this is to prevent massive amounts of tubed entities with wielders, added in sbz + local to_return = vector.add(self.start_pos, velocity) + local to_return_node = core.get_node(to_return) + local to_return_def = core.registered_nodes[to_return_node.name] + if to_return_def.tube and to_return_def.tube.can_insert and oldstack:equals(leftover) then + local can_insert = to_return_def.tube.can_insert(to_return, to_return_node, stack, velocity, self.owner) + if not can_insert then -- drop itself + local dropped_item = minetest.add_item(self.start_pos, stack) + if dropped_item then + dropped_item:set_velocity(vector.multiply(velocity, 2)) + self:remove() + end + return + end + end + + self:set_pos(vector.subtract(self.start_pos, vector.multiply(vel, moved_by - 1))) + self:set_velocity(velocity) + self:set_item(leftover:to_string()) + return + end + + local tags + if pipeworks.enable_item_tags then tags = self.tags or {} end + local found_next, new_velocity, multimode = go_next(self.start_pos, velocity, stack, self.owner, tags) -- todo: color + if pipeworks.enable_item_tags then self.tags = #tags > 0 and tags or nil end + local rev_vel = vector.multiply(velocity, -1) + local rev_dir = vector.direction(self.start_pos, vector.add(self.start_pos, rev_vel)) + local rev_node = minetest.get_node(vector.round(vector.add(self.start_pos, rev_dir))) + local tube_present = minetest.get_item_group(rev_node.name, 'tubedevice') == 1 + if not found_next then + if + pipeworks.drop_on_routing_fail + or not tube_present + or minetest.get_item_group(rev_node.name, 'tube') ~= 1 + then + -- Using add_item instead of item_drop since this makes pipeworks backward + -- compatible with Minetest 0.4.13. + -- Using item_drop here makes Minetest 0.4.13 crash. + local dropped_item = minetest.add_item(self.start_pos, stack) + if dropped_item then + dropped_item:set_velocity(vector.multiply(velocity, 5)) + self:remove() + end + return + else + velocity = vector.multiply(velocity, -1) + self:set_pos(vector.subtract(self.start_pos, vector.multiply(vel, moved_by - 1))) + self:set_velocity(velocity) + end + elseif is_multimode(multimode) then + -- create new stacks according to returned data. + local s = self.start_pos + for _, split in ipairs(multimode) do + pipeworks.tube_inject_item(s, s, split.velocity, split.itemstack, self.owner) + end + -- remove ourself now the splits are sent + self:remove() + return + end + + if new_velocity and not vector.equals(velocity, new_velocity) then + local nvelr = math.abs(new_velocity.x + new_velocity.y + new_velocity.z) + self:set_pos(vector.add(self.start_pos, vector.multiply(new_velocity, (moved_by - 1) / nvelr))) + self:set_velocity(new_velocity) + end + end, }) -if minetest.get_modpath("mesecons_mvps") then - mesecon.register_mvps_unmov("pipeworks:tubed_item") - mesecon.register_mvps_unmov("pipeworks:color_entity") - mesecon.register_on_mvps_move(function(moved_nodes) - local moved = {} - for _, n in ipairs(moved_nodes) do - moved[minetest.hash_node_position(n.oldpos)] = vector.subtract(n.pos, n.oldpos) - end - for _, entity in pairs(luaentity.entities) do - if entity.name == "pipeworks:tubed_item" then - local pos = entity:get_pos() - local rpos = vector.round(pos) - local dir = moved[minetest.hash_node_position(rpos)] - if dir then - entity:set_pos(vector.add(pos, dir)) - entity.start_pos = vector.add(entity.start_pos, dir) - end - end - end - end) +if minetest.get_modpath 'mesecons_mvps' then + mesecon.register_mvps_unmov 'pipeworks:tubed_item' + mesecon.register_mvps_unmov 'pipeworks:color_entity' + mesecon.register_on_mvps_move(function(moved_nodes) + local moved = {} + for _, n in ipairs(moved_nodes) do + moved[minetest.hash_node_position(n.oldpos)] = vector.subtract(n.pos, n.oldpos) + end + for _, entity in pairs(luaentity.entities) do + if entity.name == 'pipeworks:tubed_item' then + local pos = entity:get_pos() + local rpos = vector.round(pos) + local dir = moved[minetest.hash_node_position(rpos)] + if dir then + entity:set_pos(vector.add(pos, dir)) + entity.start_pos = vector.add(entity.start_pos, dir) + end + end + end + end) end diff --git a/mods/sbz_planets/mod.conf b/mods/sbz_planets/mod.conf index 30c13cf4..5e91f86f 100644 --- a/mods/sbz_planets/mod.conf +++ b/mods/sbz_planets/mod.conf @@ -1,2 +1,2 @@ name = sbz_planets -depends = sbz_base, biomegen, sbz_resources, sbz_chem, sbz_bio \ No newline at end of file +depends = sbz_base, sbz_resources, sbz_chem, sbz_bio diff --git a/mods/sbz_player_settings/apply.lua b/mods/sbz_player_settings/apply.lua index ea190c00..83768a98 100644 --- a/mods/sbz_player_settings/apply.lua +++ b/mods/sbz_player_settings/apply.lua @@ -12,6 +12,7 @@ receive(function(player, formname, fields) local pmeta = player:get_meta() local new_value = math.min(200, math.max(0, scrollbar.value)) pmeta:set_int('bgm_volume', new_value) + pmeta:set_int('has_set_volume', 1) local handle = sbz_api.bgm_handles[player:get_player_name()] if handle then core.sound_fade(handle, 4, (new_value / 100) + 0.001) end -- HACK: +0.001 so that it doesn't delete the sound diff --git a/mods/sbz_power/infinite_storinator.lua b/mods/sbz_power/infinite_storinator.lua index b9d0fc37..37e01e8e 100644 --- a/mods/sbz_power/infinite_storinator.lua +++ b/mods/sbz_power/infinite_storinator.lua @@ -1,153 +1,10 @@ --- License for the function make_scrollbaroptions_for_scroll_container: ---[[ -License of minetest: https://github.com/minetest/minetest/blob/master/LICENSE.txt - -Minetest -Copyright (C) 2022 rubenwardy - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Only applies to the function below, everything else is licensed normally as in LICENSE.txt -]] ---- Creates a scrollbaroptions for a scroll_container --- --- @param visible_l the length of the scroll_container and scrollbar --- @param total_l length of the scrollable area --- @param scroll_factor as passed to scroll_container -local function make_scrollbaroptions_for_scroll_container(visible_l, total_l, scroll_factor) - assert(total_l >= visible_l) - local max = total_l - visible_l - local thumb_size = (visible_l / total_l) * max - return ("scrollbaroptions[min=0;max=%f;thumbsize=%f]"):format(max / scroll_factor, thumb_size / scroll_factor) -end - -local max_slots = 5000 - -local M = minetest.get_meta - -local slots_per_1_power = 8 - -sbz_api.register_machine("sbz_power:infinite_storinator", { - description = "Infinite Storinator (deprecated)", - info_extra = { - "If you loose power you will need to re-power it to get your items back", - "For one power you can get " .. slots_per_1_power .. " slots" - }, - tiles = { - "infinite_storinator_side.png", - "infinite_storinator_side.png", - "infinite_storinator_side.png", - "infinite_storinator_side.png", - "infinite_storinator_side.png", - { name = "infinite_storinator_front.png", animation = { type = "vertical_frames", length = 2 } } - }, - paramtype2 = "facedir", - on_construct = function(pos) - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - inv:set_size("inv", max_slots) - meta:set_int("visible_slots", 0) - meta:set_int("slots_set", 8) - end, +sbz_api.register_machine('sbz_power:infinite_storinator', { + description = 'Infinite Storinator (deprecated)', + paramtype2 = 'facedir', groups = { matter = 1, not_in_creative_inventory = 1 }, + drop = '', action = function(pos, node, meta, supply, demand) - -- time to nuke supply :D - if supply <= demand then return 0 end - - local slots = 0 - local max_slots_allowed = math.floor((supply - demand) * slots_per_1_power) - local slots_set = math.floor(meta:get_int("slots_set")) - if max_slots_allowed < slots_set then - slots = max_slots_allowed - else - slots = slots_set - end - local power_consumed = slots / slots_per_1_power - - meta:set_int("visible_slots", slots) - meta:set_string("infotext", string.format("Infinite storinator, slots: %s, consuming: %s", slots, power_consumed)) - - local max_width = 8 - - local list_inv_w = max_width - local list_inv_h = math.floor(slots / max_width) - - - - meta:set_string("formspec", string.format([[ - formspec_version[7] - size[10.6,15.2] - %s - style_type[list;noclip=false;size=1,1;spacing=0.25,0.25] - scrollbar[10,0.2;0.4,8.6;vertical;scrollbar;0] - scroll_container[0.2,0.2;9.8,8.6;scrollbar;vertical;1] - list[context;inv;0,0;%s,%s] - scroll_container_end[] - list[current_player;main;0.2,9;8,4;] - field[0.2,14.4;3,0.5;set_slots;Amount of rows;%s] - listring[] - ]], make_scrollbaroptions_for_scroll_container(7.6, math.max(8, list_inv_h + list_inv_h / 4), 1), list_inv_w, - list_inv_h, - slots_set / 8)) - return power_consumed - end, - on_receive_fields = function(pos, _, fields) - local meta = minetest.get_meta(pos) - if fields.set_slots then - local n_slots = math.abs(math.floor(tonumber(fields.set_slots) or 8)) * 8 - if n_slots > max_slots then - n_slots = max_slots - end - meta:set_int("slots_set", n_slots) - end - end, - allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) - local meta = M(pos) - if (from_index <= meta:get_int("visible_slots") and to_index <= meta:get_int("visible_slots")) or (from_list == to_list == "main") then - return count - end - return 0 - end, - - allow_metadata_inventory_put = function(pos, listname, index, stack, player) - local meta = M(pos) - if index <= meta:get_int("visible_slots") or listname == "main" then - return stack:get_count() - end - return 0 - end, - allow_metadata_inventory_take = function(pos, listname, index, stack, player) - local meta = M(pos) - if index <= meta:get_int("visible_slots") or listname == "main" then return stack:get_count() end return 0 end, - output_inv = "inv", - input_inv = "inv", - - - control_action_raw = true, - disallow_pipeworks = false, -}) - ---[[ -minetest.register_craft({ - output = "sbz_power:infinite_storinator", - recipe = { - { "sbz_resources:storinator", "sbz_resources:emittrium_circuit", "sbz_resources:storinator" }, - { "sbz_resources:storinator", "sbz_meteorites:neutronium", "sbz_resources:storinator" }, - { "sbz_resources:storinator", "sbz_resources:emittrium_circuit", "sbz_resources:storinator" } - } + disallow_pipeworks = true, }) ---]] diff --git a/mods/sbz_power/init.lua b/mods/sbz_power/init.lua index e48d49bd..1650eba7 100644 --- a/mods/sbz_power/init.lua +++ b/mods/sbz_power/init.lua @@ -87,10 +87,10 @@ function sbz_api.register_machine(name, def) meta:set_string('infotext', 'Not enough power, needs: ' .. def.power_needed) return def.power_needed else - meta:set_string('infotext', 'Running') local power_consumed = old_action(pos, node, meta, supply, demand) or def.idle_consume or def.power_needed + meta:set_string('infotext', ('Running (%s)'):format(sbz_api.format_power(power_consumed))) return power_consumed end diff --git a/mods/sbz_power/manual_crafter.lua b/mods/sbz_power/manual_crafter.lua index 0ca39efd..99ee270a 100644 --- a/mods/sbz_power/manual_crafter.lua +++ b/mods/sbz_power/manual_crafter.lua @@ -53,6 +53,7 @@ end -- aux1+punch -- craft 10 -- punch - craft 1 -- Do not use to implement some sort of autocrafter this may perform badly at scale +---@param user core.PlayerRef local function craft(user, meta) local control = user:get_player_control() local craft_amount = 1 @@ -121,6 +122,23 @@ local function craft(user, meta) end if can_craft > 0 then + --- Prepare for crafting + for name, amount in pairs(required_items) do + local remove_amount = tonumber(amount * can_craft) + local max_stack = ItemStack(name):get_stack_max() + + local stacks = remove_amount / max_stack + local remaining = (stacks - math.floor(stacks)) * max_stack + stacks = math.floor(stacks) + + if stacks > 0 then + for _ = 1, stacks do + user_inv:remove_item('main', name .. ' ' .. max_stack) + end + end + if remaining > 0 then user_inv:remove_item('main', name .. ' ' .. remaining) end + end + items_crafted = can_craft * craft_result:get_count() local split_into = math.floor(items_crafted / craft_result_max) if split_into > 0 then @@ -199,9 +217,17 @@ core.register_node('sbz_power:manual_crafter', { local from_inv = core.get_meta(pos):get_inventory() from_inv:set_stack(listname, index, '') if listname == 'configure_craft_output' then - from_inv:set_list('configure_craft', { '', '', '', - '', '', '', - '', '', '' }) + from_inv:set_list('configure_craft', { + '', + '', + '', + '', + '', + '', + '', + '', + '', + }) else validate_craft(pos) end @@ -211,12 +237,13 @@ core.register_node('sbz_power:manual_crafter', { }) local blob = 'sbz_resources:matter_blob' +local emit = 'sbz_resources:raw_emittrium' core.register_craft { type = 'shaped', output = 'sbz_power:manual_crafter', recipe = { - { blob, blob, blob }, + { emit, blob, emit }, { blob, 'sbz_resources:simple_circuit', blob }, - { blob, blob, blob }, + { emit, blob, emit }, }, } diff --git a/mods/sbz_progression/annoy.lua b/mods/sbz_progression/annoy.lua index 6146e3a5..ad991bd9 100644 --- a/mods/sbz_progression/annoy.lua +++ b/mods/sbz_progression/annoy.lua @@ -6,11 +6,13 @@ minetest.register_globalstep(function(dtime) for _, player in ipairs(minetest.get_connected_players()) do local inv = player:get_inventory() if inv then - if not inv:contains_item("main", "sbz_progression:questbook") then - if player:get_meta():get_int("questbookwarning") == 0 then - sbz_api.displayDialogLine(player:get_player_name(), - "Lost your questbook? Use /qb to get it back.") - player:get_meta():set_int("questbookwarning", 1) + if not inv:contains_item('main', 'sbz_progression:questbook') then + if player:get_meta():get_int 'questbookwarning' == 0 then + sbz_api.displayDialogLine( + player:get_player_name(), + 'Lost your questbook? Use /qb to get it back.' + ) + player:get_meta():set_int('questbookwarning', 1) end end end @@ -18,21 +20,21 @@ minetest.register_globalstep(function(dtime) end end) -minetest.register_chatcommand("qb", { +minetest.register_chatcommand('qb', { description = "Gives you a questbook if you don't have one.", privs = {}, func = function(name, param) local inv = minetest.get_player_by_name(name):get_inventory() - if inv then - if inv:contains_item("main", "sbz_progression:questbook") then - sbz_api.displayDialogLine(name, "You already have a Quest Book.") + if not inv then return end + + if inv:contains_item('main', 'sbz_progression:questbook') then + sbz_api.displayDialogLine(name, 'You already have a Quest Book.') + else + if inv:room_for_item('main', 'sbz_progression:questbook') then + inv:add_item('main', 'sbz_progression:questbook') + sbz_api.displayDialogLine(name, 'You have been given a Quest Book.') else - if inv:room_for_item("main", "sbz_progression:questbook") then - inv:add_item("main", "sbz_progression:questbook") - sbz_api.displayDialogLine(name, "You have been given a Quest Book.") - else - sbz_api.displayDialogLine(name, "Your inventory is full.") - end + sbz_api.displayDialogLine(name, 'Your inventory is full.') end end end, diff --git a/mods/sbz_progression/quests/Chemistry.md b/mods/sbz_progression/quests/Chemistry.md index da7586ec..c660b434 100644 --- a/mods/sbz_progression/quests/Chemistry.md +++ b/mods/sbz_progression/quests/Chemistry.md @@ -53,28 +53,31 @@ Congratulations, Commander! You've just unlocked the Bronze Age, because nothing Requires: Simple Alloy Furnace, Crusher -## Compressor +## Centrifuge ### Text -Have you ever wanted to turn your metals into blocks? Now you can! -Craft this wonderful compressor, insert 9 powder, or 9 ingots, and watch as it makes blocks out of them. - -Blocks made from metals cannot be dug with matter annihilators, you must use the robotic arm or the drill instead. +In the centrifuge you can get: +- sand -> silicon, gold, white sand +- gravel -> titanium, lithium, cobalt, pebbles +- white sand/dark sand -> silver, darker variation of said sand ### Meta -Requires: Crusher, Simple Alloy Furnace +Requires: Crusher, Simple Alloy Furnace -## Centrifuge +## Compressor ### Text -In the centrifuge you can get gold, silicon and white sand from normal sand, silver from some sand shades, and rare metals (titanium, lithium and cobalt) from gravel. +Have you ever wanted to turn your metals into blocks? Now you can! +Craft this wonderful compressor, insert 9 powder, or 9 ingots, and watch as it makes blocks out of them. +Blocks made from metals cannot be dug with matter annihilators, you must use the robotic arm or the drill instead. ### Meta -Requires: Crusher, Simple Alloy Furnace +Requires: Crusher, Simple Alloy Furnace + ## Advanced Batteries @@ -90,7 +93,7 @@ Requires: Batteries, Furnace, Centrifuge ### Text -If you have lots of metals this is the battery for you! +If you have **lots of metals** this is the battery for you! If you don't, then those metals can be put into better uses. ### Meta @@ -165,7 +168,6 @@ Requires: Furnace ### Text The elecric drill has 500 uses and is powered by electricity. -What does that mean? If you right click on a battery with the electric drill in your hand, it will take power from the battery and charge the drill! (Just like the jetpack) It needs 1 power per 1 use. @@ -358,10 +360,11 @@ Requires: Crusher Small protectors protect a decently sized area. Unwanted people won't be able to take items from machines or modify filter injectors or... like... do anything in your land... if the area is protected. Also this can't be placed anywhere near cores. -Special names ("owners") that you can add to protectors (no player can name themselvs these): +Special names ("owners") that you can add to protectors (no player can name themselves these): **.meteorite** - meteorites will now explode in your area **.strange_blob_spread** - strange blobs will spread in your area + ### Meta Requires: Concrete Plan, Furnace diff --git a/mods/sbz_progression/quests/Introduction.md b/mods/sbz_progression/quests/Introduction.md index add55432..aab5f467 100644 --- a/mods/sbz_progression/quests/Introduction.md +++ b/mods/sbz_progression/quests/Introduction.md @@ -22,8 +22,8 @@ Some commands useful for survival: - `/qb` - gives you the questbook if you don't have one - `/home` - teleports you to home - `/sethome` - sets your home -- `/drawers_fix` - if drawers appear broken just run that, command was taken from pandorabox_custom -- `/toggle_areas_hud` - Toggles the areas hud, may be useful if you dont want to always see it. +- `/drawers_fix` - if drawers appear broken just run that, yes that command is from pandorabox_custom +- `/toggle_areas_hud` - Toggles the areas hud, useful if you don't want to always see it. - `/bgm_volume` \ - sets the background music's volume **Hotbar switching** @@ -61,18 +61,18 @@ Requires: ### Text -Welcome, player. This is the Quest Book. Here, you can check out what tasks you have to do, and the materials you will need for each quest. - +Welcome, player. This is the Quest Book. Here, you can check out what tasks you can do and information about them. + You can also just ignore the Quest Book if you are an experienced player. - + Now, to get started: look down at the core. Punching it will give you some of your first resources. -You can also just **hold right-click on the core**, it will be easier on you. - +You can also **hold right-click on the core**, it will be easier on you. + These quests are in no particular order, but you might need items from one quest for another. The Introduction questline is designed to get you started into a couple other questlines, so it is recommended you jump between questlines occasionally. - + If you need to know a recipe, search the item in the inventory, and click on it, it will bring up what it can be used for or how it can be crafted. - + TIP: If you lose your Quest Book, you can use /qb to get it back. ### Meta @@ -84,7 +84,7 @@ Requires: ### Text Isn't this one node a little too crammed? Let's do something about that. - + Punch the Core a little more. With nine 'Matter Dust', you can craft yourself a 'Matter Blob'. If you are unable to place a matter blob next to the core, try sneaking while placing it. On multiplayer servers the area around the core may be protected. @@ -108,7 +108,6 @@ Requires: Introduction ### Text Doesn't it feel weird to be holding antimatter? To break nodes you'll need a **Matter** Annihilator, you should craft it up now since it's used in some other crafts down the line. -TIP: Half-broken Annihilators can also be used to craft with. Also, make sure you are crafting the **Matter** annihilator, not the **Anti**matter annihilator. @@ -160,7 +159,7 @@ Requires: A bigger platform ### Text The Switching Station is an important node, because it is the heart of any Power Grid. -You need to have one switching station per power grid. +You **need** to have **one** switching station per power grid. Every machine needs to be connected to a switching station. The Switching Station also displays statistics about the Power Grid when hovered over in-world. When a machine says "no network found", it's not connected to the switching station and won't do anything. @@ -225,8 +224,8 @@ Requires: A bigger platform ### Text -Manual crafters are nodes that allow you to craft things very quickly. They do not require power, and **CANNOT be used with automation**. -They also won't work with crafting recipes that have "replacements", it will instead simply consume those items. (Recipes like that are very rare in sbz.) +Manual crafters are nodes that allow you to craft things very quickly. They do not require power, and **cannot** be used with automation. +They also won't work with crafting recipes that have "replacements", it will instead simply consume those items. (Recipes like that are very rare in sbz, unless you modified it.) **Controls:** - `place`: Configure @@ -260,6 +259,8 @@ Retaining Circuits are a type of Circuit often used in nodes which store items, Unlike Simple Circuits, this will only craft one Retaining Circuit. +TIP: With retaining circuits, you can craft **storinators** to permanently store items outside of your inventory. (They are documented in the storage questline) + ### Meta Requires: Antimatter, Circuitry @@ -270,7 +271,7 @@ Requires: Antimatter, Circuitry We're making the jump from generic matter to stone now! Here is where building a space station gets fun! -First, before we can make Stone nodes we will need Pebbles. They are quite difficult to make, requiring three matter blobs, so **having at least 4 advanced matter extractors is recommended**, as you will need a lot of pebbles. +First, before we can make Stone nodes we will need Pebbles. They are quite difficult to make, requiring **three matter blobs**, so **having at least 4 advanced matter extractors is recommended**, as you will need a lot of pebbles. Pebbles will unlock a lot of decorational nodes to spice up your island, as well as plenty of tech, and if you want you can even start building your own planet. It's all up to your imagination! ### Meta @@ -296,9 +297,9 @@ Requires: Pretty Pebbles The questbook isn't meant to be explored linearly. You should do quests in whichever order you like. If you have inventory issues, the storage questline is within your reach. If you want to explore more machines, check out the emittrium and chemistry questline. -You should also check out the meteorites questline, if you don't, you will wish you saw it sooner. +You should also check out the meteorites questline, if you don't, you might wish that you saw it sooner. If you are a decorator, there is a questline for you too. -If you want to actually automate (you know, the fun part), see the pipeworks questline. **Do that as soon as you can.** +If you want to actually automate (you know, the fun part), see the pipeworks questline. Do that as soon as you can. You can also filter reachable quests (Quests that you can see the descriptions of, but haven't completed yet) by typing "reachable" into the questbook search bar diff --git a/mods/sbz_progression/quests/Meteorites.md b/mods/sbz_progression/quests/Meteorites.md index ff8146ae..9f1c437c 100644 --- a/mods/sbz_progression/quests/Meteorites.md +++ b/mods/sbz_progression/quests/Meteorites.md @@ -6,7 +6,11 @@ ### Text -By this point you've probably been here for at least an hour. You've almost certainly noticed the funny meteorites that whiz past your core occasionally. These are actually a source of metal as well. If you can stop them, which is really hard when you don't know where they're going. But with the alloys you've got, you can craft Meteorite Radar, which shows you their trajectory and makes them much easier to catch. When there is no meteorite nearby, it will do nothing. It'll probably still take a few tries though. When meteorites explode, they will turn into neutronium and some meteoric matter, all meteoric matter decays after a hour. Neutronium or strange matter do not decay. +By this point you've probably been here for at least an hour. You've almost certainly noticed the funny meteorites that whiz past your core occasionally. +These are actually a source of metal as well. If you can stop them, which is really hard when you don't know where they're going. But with the alloys you've got, you can craft a **Meteorite Radar**, which shows you their trajectory and makes them much easier to catch. +When there is no meteorite nearby, it will do nothing. It'll probably still take a few tries though. When meteorites explode, they will turn into neutronium and some meteoric matter, all meteoric matter decays after a hour. Neutronium or strange matter do not decay. + +TIP: The meteorite radar consumes **40 Cj** when a meteorite is nearby, but it will consume only **10 Cj** if it can't detect a meteorite. Be sure to have a battery when using it. ### Meta @@ -16,9 +20,8 @@ Requires: Simple Alloy Furnace, Emittrium Circuits ### Text -Soo... i'm guessing you would have a lot of trouble getting to the meteorites by bridging... -Well don't fear, there is the laser now... - +The laser is a tool that allows you to shoot down meteorites from far away. It is useful if you have trouble bridging/jumping to meteorites. + To charge the laser, "place it" into a battery. (right-click the battery with it in your hand) It also does some damage to other players if PVP is enabled. @@ -31,7 +34,7 @@ Requires: Simple Alloy Furnace, Emittrium Circuits ### Text In the core of a meteorite, you can find a single piece of very dense matter called Neutronium. It's so dense that you can craft it into a Gravitational Attractor, which attracts other passing meteorites and gets you even more metal, or a Gravitational Repulsor which drives them away. -To complete this quest, make a gravitational attractor, or a repulsor. +To complete this quest, make a Gravitational attractor. ### Meta @@ -43,6 +46,8 @@ Requires: Meteorites Sometimes, meteorites whizzing by will be made of antimatter instead of regular matter. These meteorites have Antineutronium in their core. Antineutronium can be crafted into a Gravitational Repulsor, which repulses meteorites. + +To complete this quest, make a Gravitational Repulsor. TIP: Build a shield out of compressed core dust to protect against antimatter meteorites. @@ -68,7 +73,7 @@ Requires: Autocrafters, Neutronium ### Text -Be aware, strange matter can... spread... to both matter and antimatter... +Strange matter can spread to both matter and antimatter. Strange matter won't spread to "charged" nodes or machines, or protected areas. So it's best to protect your area, even if you are in singleplayer, to defend against strange matter. diff --git a/mods/sbz_progression/quests/Pipeworks_and_fluid_transport.md b/mods/sbz_progression/quests/Pipeworks_and_fluid_transport.md index 7da37897..8cd0e6fe 100644 --- a/mods/sbz_progression/quests/Pipeworks_and_fluid_transport.md +++ b/mods/sbz_progression/quests/Pipeworks_and_fluid_transport.md @@ -89,7 +89,7 @@ Requires: Automatic Filter-Injectors, Bear Arms, Emittrium Circuits ### Text -Autocrafters automatically craft. They require a crafting processor to run, which you can upgrade as you progress through the game, to increase crafting speed. +Autocrafters automatically craft. They require a crafting processor item to run, which you can upgrade as you progress through the game, to increase crafting speed. ### Meta @@ -116,7 +116,6 @@ Requires: Autocrafters ### Text Item voids delete every item that goes in, and yes these are pipeworks trash cans. But unlike pipeworks trash cans, they show the amount of items they've destroyed. -That number can "overflow" into the negatives, if you actually manage to do this, don't consider it a bug, but consider it an achievement :) ### Meta @@ -133,15 +132,15 @@ Then, there might be an issue with it if the storinator that we are putting item In cases where there is only one tube, the item will simply drop, but when there are at least 2 tubes, the items will wonder around, until eventually there will be too many of them. In that case, the tubes will break. How do we prevent our tubes breaking, or items dropping? -Well... a simple answer would be to have more storinators :D... but that's not practical +Wel a simple answer would be to have more storinators :D..... but that's not practical **Instead, consider using item voids like this:** \ Item voids have the lowest priority, so items really don't want to go there. -Storinator, has a higher priority than the item void, so if the storinator isn't full, items will go there. +Storinator, has a higher priority than the item void, so if the storinator isn't full, items will want to go there. Item voids, have a lower priority, so if the item can't go to the storinator, it will go into the item void. -The default priority is 100. +The default priority of nodes is 100. ### Meta diff --git a/mods/sbz_progression/quests/Storage.md b/mods/sbz_progression/quests/Storage.md index 6d6a5dd8..ebf2919d 100644 --- a/mods/sbz_progression/quests/Storage.md +++ b/mods/sbz_progression/quests/Storage.md @@ -1,8 +1,6 @@ - # Questline: Storage -So... i assume you need some sort of storage... to store items.... Well don't worry, Skyblock Zero provides multiple ways to do that. - +Mainly about drawers and storinators. ## Storinators @@ -19,8 +17,8 @@ Requires: Matter Plates, Charged Field, Retaining Circuits ### Text -Soo... i don't think the storinator's 32 slots is enough for you, is it? -Well don't worry.. you can craft more storinators.... OR you can upgrade them :\> +At some point only having 32 slots of storage is not enough. +You can make more storinators, or you can make an upgraded one. Each upgrade adds 1 row and 1 collumn to the inventory space. To complete this quest, you need to craft the bronze storinator. @@ -43,8 +41,8 @@ Requires: Storinators ### Text -Do you have chaos in your storinators, are you struggling to figure out which one is which.... Do you need labels, but signs get in the way? -Well, you can use neutronium storinators, they are a little expensive, requiring 4 neutronium, but if you can afford them, they are great for this. +Do you have chaos in your storinators, are you struggling to figure out which one is which, do you need labels, but signs get in the way? +You can use neutronium storinators, they are a little expensive, requiring 4 neutronium, but if you can afford them, they are great for this. ### Meta @@ -54,17 +52,13 @@ Requires: Better Storinators, Neutronium ### Text -Do you have some some sort of automation set-up, that produces LARGE amounts of 1 type of item? -Or do you just have a large amount of some junk, but you don't want to throw it away for some reason? - -Well... introducing DRAWERS, they are like storinators... but only store 1 type of item... and don't store tools... -But yeah they are really good at that... -They store 3200 of 1 type of item (without upgrades). With upgrades they can store a lot more. - +Do you have some some automation that produces large amounts of 1 type of item? +Or do you just have large amounts of one item type? + +Drawers will help here, they are like storinators but they can only store a few item types. +They store 3200 items (without upgrades). With upgrades they can store a lot more. + Drawers also come with 1x2 and 4x4 variants. - -**Tip: These support the automatic filter injector.** -**Tip: Unlike the regular minetest-mods/drawers, these ones can't get dug when they contain something** ### Meta @@ -74,10 +68,10 @@ Requires: Storinators ### Text -Soo... i'm guessing that one drawer just won't meet all of your needs... well... -You can upgrade it! By clicking on the sides of the drawer, or the edges (if you are only facing the front fase) you can see an inventory, in which you put the upgrades... - -The upgrades don't work as you expect them to, if you have 2 bronze upgrades, each bronze upgrade adds an extra 3200 items, it doesn't multiply 3200 by 4 +Drawers by default store the same amount of items as a storinator, that's a bit boring. What if you could store more? + +TIP: The upgrades don't work as you expect them to, if you have 2 bronze upgrades, each bronze upgrade adds an extra 3200 items, it doesn't multiply 3200 by 4 or anything like that. +TIP: To insert the upgrade into a drawer, you need to right-click the edge of it, or right-click a side that doesn't have the item display. ### Meta @@ -89,9 +83,9 @@ Requires: Drawers, Bronze Age I think you have noticed that drawers still may not work so amazingly for automation, the drawer controller is here to solve that. If you send it an item with a tube, or manually provide it an item, it will automatically put it in one of the drawers near it. - + For taking out items, you can use a luacontroller, sending an itemstack (a string like "sbz_bio:dirt 100"), and it will try to give you that dirt by injecting it out of itself. ### Meta -Requires: Drawers \ No newline at end of file +Requires: Drawers diff --git a/mods/sbz_resources/init.lua b/mods/sbz_resources/init.lua index b13b9f75..105235c4 100644 --- a/mods/sbz_resources/init.lua +++ b/mods/sbz_resources/init.lua @@ -1,18 +1,19 @@ -local modpath = minetest.get_modpath("sbz_resources") +local modpath = minetest.get_modpath 'sbz_resources' -- dofiles -dofile(modpath .. "/basic_resources.lua") -dofile(modpath .. "/emitters.lua") -dofile(modpath .. "/nodes.lua") -dofile(modpath .. "/water.lua") -dofile(modpath .. "/tools.lua") -dofile(modpath .. "/storinators.lua") -dofile(modpath .. "/items.lua") -dofile(modpath .. "/logic_craftitems.lua") -dofile(modpath .. "/jetpack.lua") -dofile(modpath .. "/strange_matter.lua") -dofile(modpath .. "/fireworks.lua") -dofile(modpath .. "/laser.lua") -dofile(modpath .. "/bomb.lua") -dofile(modpath .. "/crystals.lua") -dofile(modpath .. "/processors_and_circuits.lua") +dofile(modpath .. '/basic_resources.lua') +dofile(modpath .. '/emitters.lua') +dofile(modpath .. '/nodes.lua') +dofile(modpath .. '/water.lua') +dofile(modpath .. '/tools.lua') +dofile(modpath .. '/storinators.lua') +dofile(modpath .. '/items.lua') +dofile(modpath .. '/logic_craftitems.lua') +dofile(modpath .. '/jetpack.lua') +dofile(modpath .. '/strange_matter.lua') +dofile(modpath .. '/fireworks.lua') +dofile(modpath .. '/laser.lua') +dofile(modpath .. '/bomb.lua') +dofile(modpath .. '/crystals.lua') +dofile(modpath .. '/processors_and_circuits.lua') +dofile(modpath .. '/parkour.lua') diff --git a/mods/sbz_resources/jetpack.lua b/mods/sbz_resources/jetpack.lua index b7ff31e8..329e87b9 100644 --- a/mods/sbz_resources/jetpack.lua +++ b/mods/sbz_resources/jetpack.lua @@ -1,45 +1,43 @@ -- this doesn't use any code from techage -local jetpack_durability_s = 60 * 5 -- jetpack durability, in seconds +local jetpack_durability_s = 60 * 5 -- jetpack durability, in seconds local jetpack_velocity = vector.new(0, 15, 0) -- multiplied by dtime -local jetpack_full_charge = 20000 -- 20kcj power needed for a jetpack +local jetpack_full_charge = 20000 -- 20kcj power needed for a jetpack local jetpack_durability_save_during_sneak_flight = 2 local default_number_of_particles = 20 local jetpack_boost = 3 local jetpack_users = {} +sbz_api.jetpack_users = jetpack_users local jetpack_charge_per_1_wear = (jetpack_full_charge / 65535) - local function edit_stack_image(user, stack) - if stack:get_name() == "sbz_resources:jetpack" then + if stack:get_name() == 'sbz_resources:jetpack' then if jetpack_users[user] then - stack:get_meta():set_string("inventory_image", "jetpack_on.png") + stack:get_meta():set_string('inventory_image', 'jetpack_on.png') else - stack:get_meta():set_string("inventory_image", "jetpack_off.png") + stack:get_meta():set_string('inventory_image', 'jetpack_off.png') end end end -minetest.register_tool("sbz_resources:jetpack", { - description = "Jetpack", - info_extra = "Idea originated from techage", - inventory_image = "jetpack_off.png", +minetest.register_tool('sbz_resources:jetpack', { + description = 'Jetpack', + info_extra = 'Idea originated from techage', + inventory_image = 'jetpack_off.png', stack_max = 1, tool_capabilities = {}, -- No specific tool capabilities, as it's not meant for digging wear_color = { - blend = "constant", + blend = 'constant', color_stops = { - [0] = "lime" - } + [0] = 'lime', + }, }, groups = { disable_repair = 1, power_tool = 1 }, - wear_represents = "power", + wear_represents = 'power', on_use = function(itemstack, user, pointed_thing) -- Check if user is valid - if not user or user.is_fake_player then - return itemstack - end + if not user or user.is_fake_player then return itemstack end local wear = itemstack:get_wear() local username = user:get_player_name() if wear < 65535 then @@ -49,7 +47,7 @@ minetest.register_tool("sbz_resources:jetpack", { jetpack_users[username] = user:get_wield_index() end else - minetest.chat_send_player(user:get_player_name(), "Jetpack ran out of charge") + minetest.chat_send_player(user:get_player_name(), 'Jetpack ran out of charge') end edit_stack_image(username, itemstack) return itemstack @@ -59,84 +57,80 @@ minetest.register_tool("sbz_resources:jetpack", { end), }) - - local speed = player_monoids.speed minetest.register_globalstep(function(dtime) for player in pairs(jetpack_users) do local real_player = minetest.get_player_by_name(player) if real_player and real_player:is_valid() then local slot = jetpack_users[player] - local jetpack_item = real_player:get_inventory():get_stack("main", slot) - if jetpack_item:get_name() ~= "sbz_resources:jetpack" then - jetpack_users[player] = nil - end - if jetpack_item:get_wear() >= 65535 then - jetpack_users[player] = nil - end + local jetpack_item = real_player:get_inventory():get_stack('main', slot) + if jetpack_item:get_name() ~= 'sbz_resources:jetpack' then jetpack_users[player] = nil end + if jetpack_item:get_wear() >= 65535 then jetpack_users[player] = nil end local controls = real_player:get_player_control() local num_particles = 0 if (controls.sneak or controls.aux1) and controls.jump and jetpack_users[player] then - speed:add_change(real_player, jetpack_boost, "sbz_resources:jetpack_boost") + speed:add_change(real_player, jetpack_boost, 'sbz_resources:jetpack_boost') real_player:add_velocity((jetpack_velocity / 2) * dtime) - jetpack_item:set_wear(math.min(65535, - jetpack_item:get_wear() + - 65535 * - ((jetpack_durability_s * (1 / jetpack_durability_save_during_sneak_flight)) ^ -1) - * dtime)) -- this works, do not question it + jetpack_item:set_wear( + math.min( + 65535, + jetpack_item:get_wear() + + 65535 + * ((jetpack_durability_s * (1 / jetpack_durability_save_during_sneak_flight)) ^ -1) + * dtime + ) + ) -- this works, do not question it num_particles = default_number_of_particles / 5 elseif controls.jump and jetpack_users[player] then - speed:add_change(real_player, jetpack_boost, "sbz_resources:jetpack_boost") + speed:add_change(real_player, jetpack_boost, 'sbz_resources:jetpack_boost') real_player:add_velocity(jetpack_velocity * dtime) - jetpack_item:set_wear(math.min(65535, - jetpack_item:get_wear() + ((65535 * (jetpack_durability_s ^ -1))) * dtime)) -- this works, do not question it + jetpack_item:set_wear( + math.min(65535, jetpack_item:get_wear() + (65535 * (jetpack_durability_s ^ -1)) * dtime) + ) -- this works, do not question it num_particles = default_number_of_particles else - speed:del_change(real_player, "sbz_resources:jetpack_boost") + speed:del_change(real_player, 'sbz_resources:jetpack_boost') end edit_stack_image(player, jetpack_item) - real_player:get_inventory():set_stack("main", slot, jetpack_item) + real_player:get_inventory():set_stack('main', slot, jetpack_item) if num_particles ~= 0 then -- make a effect local vel = real_player:get_velocity() vel = vector.subtract(vector.zero(), vel) - minetest.add_particlespawner({ + minetest.add_particlespawner { amount = num_particles, time = dtime, - texture = "star.png", + texture = 'star.png', texpool = { - "star.png^[colorize:red", - "star.png^[colorize:blue", - "star.png^[colorize:green", + 'star.png^[colorize:red', + 'star.png^[colorize:blue', + 'star.png^[colorize:green', }, exptime = { min = 1, max = 2 }, vel = { min = vector.new(-2, -2, -2), max = vector.new(2, 2, 2) }, acc = { min = vel, max = vel * 5 }, radius = { min = 0.1, max = 0.3, bias = 1 }, glow = 14, - pos = real_player:get_pos() - }) + pos = real_player:get_pos(), + } end else jetpack_users[player] = nil end end for k, v in ipairs(minetest.get_connected_players()) do - if not jetpack_users[v:get_player_name()] then - speed:del_change(v, "sbz_resources:jetpack_boost") - end + if not jetpack_users[v:get_player_name()] then speed:del_change(v, 'sbz_resources:jetpack_boost') end end end) - minetest.register_craft { - output = "sbz_resources:jetpack", + output = 'sbz_resources:jetpack', recipe = { - { "sbz_resources:emittrium_circuit", "sbz_power:battery", "sbz_resources:emittrium_circuit" }, - { "sbz_resources:angels_wing", "sbz_meteorites:neutronium", "sbz_resources:angels_wing" }, - { "sbz_resources:emittrium_circuit", "", "sbz_resources:emittrium_circuit" } - } + { 'sbz_resources:emittrium_circuit', 'sbz_power:battery', 'sbz_resources:emittrium_circuit' }, + { 'sbz_resources:angels_wing', 'sbz_meteorites:neutronium', 'sbz_resources:angels_wing' }, + { 'sbz_resources:emittrium_circuit', '', 'sbz_resources:emittrium_circuit' }, + }, } diff --git a/mods/sbz_resources/parkour.lua b/mods/sbz_resources/parkour.lua new file mode 100644 index 00000000..6af384ac --- /dev/null +++ b/mods/sbz_resources/parkour.lua @@ -0,0 +1,56 @@ +-- Here should be anything related to parkour +-- Like even ladders +-- but they are in sbz_decor +-- so im not bringing them here + +-- what should sbz_resources even be responsible for... should it have all the nodedefs/machine defs? +-- I think yes! but idk + +core.register_node( + 'sbz_resources:emittrium_block', + unifieddyes.def { + description = 'Emittrium Block', + info_extra = 'You should punch it, and place some close to eachother.', + paramtype2 = 'color', + groups = { matter = 1 }, + tiles = { 'emittrium_block.png' }, + + -- imagine if someone punches it 30 times/the same globalstep + -- now imagine me caring + on_punch = function(pos, node, puncher, _) + if not puncher:is_player() then return end + local dir = puncher:get_pos() - pos + + -- okay, so the punching strength is multiplied by how many emittrium blocks are nearby + -- could do some stupid search but i dont want to + -- so instead.. + local strength = 2 + (sbz_api.count_nodes_within_radius(pos, 'sbz_resources:emittrium_block', 1) * 2) + core.add_particlespawner { + amount = strength * 8, + time = 0.01, + texture = { name = 'star.png^[colorize:cyan', alpha = 2, scale_tween = { 1.5, 0 }, blend = 'add' }, + exptime = 2, + glow = 14, + pos = pos, + radius = 0.5, + vel = vector.multiply(dir, strength / 2), + attract = { + kind = 'point', + strength = -5, + origin = pos, + }, + } + puncher:add_velocity(vector.multiply(dir, strength)) + end, + } +) + +core.register_craft { + type = 'shaped', + output = 'sbz_resources:emittrium_block', + recipe = { + { 'sbz_resources:raw_emittrium', 'sbz_resources:raw_emittrium', 'sbz_resources:raw_emittrium' }, + { 'sbz_resources:raw_emittrium', 'sbz_resources:raw_emittrium', 'sbz_resources:raw_emittrium' }, + { 'sbz_resources:raw_emittrium', 'sbz_resources:raw_emittrium', 'sbz_resources:raw_emittrium' }, + }, +} diff --git a/mods/sbz_resources/processors_and_circuits.lua b/mods/sbz_resources/processors_and_circuits.lua index adcea415..8e453e72 100644 --- a/mods/sbz_resources/processors_and_circuits.lua +++ b/mods/sbz_resources/processors_and_circuits.lua @@ -1,213 +1,261 @@ -minetest.register_craftitem("sbz_resources:simple_circuit", { - description = "Simple Circuit", - inventory_image = "simple_circuit.png", +minetest.register_craftitem('sbz_resources:simple_circuit', { + description = 'Simple Circuit', + inventory_image = 'simple_circuit.png', stack_max = 256, }) -minetest.register_craft({ - type = "shapeless", - output = "sbz_resources:simple_circuit 2", - recipe = { "sbz_resources:core_dust", "sbz_resources:matter_blob" } -}) - -minetest.register_craftitem("sbz_resources:mosfet", { - description = "Metal-Oxide-Semiconductor Field-Effect Transistor (MOSFET)", - inventory_image = "mosfet.png", -- REPLACE THIS - stack_max = 256, -}) - -minetest.register_craft({ - type = "shapeless", - output = "sbz_resources:mosfet", - recipe = { "sbz_chem:iron_ingot", "sbz_power:power_pipe" } -}) +minetest.register_craft { + type = 'shapeless', + output = 'sbz_resources:simple_circuit 2', + recipe = { 'sbz_resources:core_dust', 'sbz_resources:matter_blob' }, +} -minetest.register_craftitem("sbz_resources:retaining_circuit", { - description = "Retaining Circuit", - inventory_image = "retaining_circuit.png", +minetest.register_craftitem('sbz_resources:retaining_circuit', { + description = 'Retaining Circuit', + inventory_image = 'retaining_circuit.png', stack_max = 256, }) -minetest.register_craft({ - type = "shapeless", - output = "sbz_resources:retaining_circuit", - recipe = { "sbz_resources:charged_particle", "sbz_resources:antimatter_dust", "sbz_resources:simple_circuit" } -}) +minetest.register_craft { + type = 'shapeless', + output = 'sbz_resources:retaining_circuit', + recipe = { 'sbz_resources:charged_particle', 'sbz_resources:antimatter_dust', 'sbz_resources:simple_circuit' }, +} -minetest.register_craftitem("sbz_resources:emittrium_circuit", { - description = "Emittrium Circuit", - inventory_image = "emittrium_circuit.png", +minetest.register_craftitem('sbz_resources:emittrium_circuit', { + description = 'Emittrium Circuit', + inventory_image = 'emittrium_circuit.png', stack_max = 256, }) -minetest.register_craft({ - type = "shapeless", - output = "sbz_resources:emittrium_circuit", - recipe = { "sbz_resources:charged_particle", "sbz_resources:retaining_circuit", "sbz_resources:raw_emittrium", "sbz_resources:matter_plate" } -}) +minetest.register_craft { + type = 'shapeless', + output = 'sbz_resources:emittrium_circuit', + recipe = { + 'sbz_resources:charged_particle', + 'sbz_resources:retaining_circuit', + 'sbz_resources:raw_emittrium', + 'sbz_resources:matter_plate', + }, +} -core.register_craftitem("sbz_resources:phlogiston_circuit", { - description = "Phlogiston Circuit", - inventory_image = "phlogiston_circuit.png" +core.register_craftitem('sbz_resources:phlogiston_circuit', { + description = 'Phlogiston Circuit', + inventory_image = 'phlogiston_circuit.png', }) core.register_craft { - type = "shapeless", - output = "sbz_resources:phlogiston_circuit 4", + type = 'shapeless', + output = 'sbz_resources:phlogiston_circuit 4', recipe = { - "sbz_resources:emittrium_circuit", "sbz_resources:emittrium_circuit", "sbz_resources:phlogiston", - "sbz_resources:emittrium_circuit", "sbz_resources:emittrium_circuit", "sbz_resources:phlogiston", - "sbz_power:simple_charged_field", "sbz_resources:antimatter_blob", "sbz_resources:compressed_core_dust", - } + 'sbz_resources:emittrium_circuit', + 'sbz_resources:emittrium_circuit', + 'sbz_resources:phlogiston', + 'sbz_resources:emittrium_circuit', + 'sbz_resources:emittrium_circuit', + 'sbz_resources:phlogiston', + 'sbz_power:simple_charged_field', + 'sbz_resources:antimatter_blob', + 'sbz_resources:compressed_core_dust', + }, } -- used in meteorite radars and weapons -core.register_craftitem("sbz_resources:prediction_circuit", { - description = "Prediction Circuit", - inventory_image = "prediction_circuit.png", +core.register_craftitem('sbz_resources:prediction_circuit', { + description = 'Prediction Circuit', + inventory_image = 'prediction_circuit.png', }) core.register_craft { - type = "shapeless", - output = "sbz_resources:prediction_circuit", + type = 'shapeless', + output = 'sbz_resources:prediction_circuit', recipe = { - "sbz_resources:emittrium_circuit", "sbz_resources:emittrium_circuit", "sbz_chem:titanium_alloy_ingot", - "sbz_resources:raw_emittrium", "sbz_resources:raw_emittrium", "sbz_resources:raw_emittrium" - } + 'sbz_resources:emittrium_circuit', + 'sbz_resources:emittrium_circuit', + 'sbz_chem:titanium_alloy_ingot', + 'sbz_resources:raw_emittrium', + 'sbz_resources:raw_emittrium', + 'sbz_resources:raw_emittrium', + }, } - -minetest.register_craftitem("sbz_resources:simple_logic_circuit", { - description = "Simple Logic Circuit", - inventory_image = "simple_logic_circuit.png", +minetest.register_craftitem('sbz_resources:simple_logic_circuit', { + description = 'Simple Logic Circuit', + inventory_image = 'simple_logic_circuit.png', stack_max = 256, }) -minetest.register_craft({ - type = "shapeless", - output = "sbz_resources:simple_logic_circuit 12", +minetest.register_craft { + type = 'shapeless', + output = 'sbz_resources:simple_logic_circuit 12', recipe = { - "sbz_bio:cleargrass", "sbz_bio:cleargrass", "sbz_bio:cleargrass", - "sbz_resources:emittrium_circuit", "sbz_resources:emittrium_circuit", "sbz_resources:emittrium_circuit", - "sbz_bio:razorgrass", "sbz_bio:razorgrass", "sbz_bio:razorgrass", - } -}) + 'sbz_bio:cleargrass', + 'sbz_bio:cleargrass', + 'sbz_bio:cleargrass', + 'sbz_resources:emittrium_circuit', + 'sbz_resources:emittrium_circuit', + 'sbz_resources:emittrium_circuit', + 'sbz_bio:razorgrass', + 'sbz_bio:razorgrass', + 'sbz_bio:razorgrass', + }, +} -minetest.register_craftitem("sbz_resources:simple_inverted_logic_circuit", { - description = "Simple Inverted Logic Circuit", - inventory_image = "simple_inverting_circuit.png", +minetest.register_craftitem('sbz_resources:simple_inverted_logic_circuit', { + description = 'Simple Inverted Logic Circuit', + inventory_image = 'simple_inverting_circuit.png', stack_max = 256, }) minetest.register_craft { - type = "shapeless", - output = "sbz_resources:simple_inverted_logic_circuit", + type = 'shapeless', + output = 'sbz_resources:simple_inverted_logic_circuit', recipe = { - "sbz_resources:compressed_core_dust", "sbz_resources:compressed_core_dust", "sbz_resources:compressed_core_dust", - "sbz_resources:compressed_core_dust", "sbz_resources:simple_logic_circuit", "sbz_resources:compressed_core_dust", - "sbz_resources:compressed_core_dust", "sbz_resources:compressed_core_dust", "sbz_resources:compressed_core_dust", - } + 'sbz_resources:compressed_core_dust', + 'sbz_resources:compressed_core_dust', + 'sbz_resources:compressed_core_dust', + 'sbz_resources:compressed_core_dust', + 'sbz_resources:simple_logic_circuit', + 'sbz_resources:compressed_core_dust', + 'sbz_resources:compressed_core_dust', + 'sbz_resources:compressed_core_dust', + 'sbz_resources:compressed_core_dust', + }, } minetest.register_craft { - type = "shapeless", - output = "sbz_resources:simple_logic_circuit", + type = 'shapeless', + output = 'sbz_resources:simple_logic_circuit', recipe = { - "sbz_resources:compressed_core_dust", "sbz_resources:compressed_core_dust", "sbz_resources:compressed_core_dust", - "sbz_resources:compressed_core_dust", "sbz_resources:simple_inverted_logic_circuit", "sbz_resources:compressed_core_dust", - "sbz_resources:compressed_core_dust", "sbz_resources:compressed_core_dust", "sbz_resources:compressed_core_dust", - } + 'sbz_resources:compressed_core_dust', + 'sbz_resources:compressed_core_dust', + 'sbz_resources:compressed_core_dust', + 'sbz_resources:compressed_core_dust', + 'sbz_resources:simple_inverted_logic_circuit', + 'sbz_resources:compressed_core_dust', + 'sbz_resources:compressed_core_dust', + 'sbz_resources:compressed_core_dust', + 'sbz_resources:compressed_core_dust', + }, } --- === PROCESSORS === -core.register_craftitem("sbz_resources:simple_processor", { - description = "Simple Processor", - inventory_image = "simple_procesor.png" -- someone correct the typo lmfao +core.register_craftitem('sbz_resources:simple_processor', { + description = 'Simple Processor', + inventory_image = 'simple_procesor.png', -- someone correct the typo lmfao }) sbz_api.recipe.register_craft { - type = "engraver", - output = "sbz_resources:simple_processor", - items = { "sbz_chem:silicon_crystal 8" } + type = 'engraver', + output = 'sbz_resources:simple_processor', + items = { 'sbz_chem:silicon_crystal 8' }, } -- crafting processors -core.register_craftitem("sbz_resources:simple_crafting_processor", { - description = "Simple Crafting Processor\nCrafts 1 item per second for 10 power.", - inventory_image = "simple_crafting_processor.png", - stack_max = 1 -}) +--[[ +DESIGN: +1st tier: don't even have metal automation +2nd tier: explored into shockshrooms +3rd tier: actively automating shock shrooms, silicon and have nuclear stuff +4th tier: bored +5th tier: and should be doing something else +]] +sbz_api.crafting_processor_stats = { + ['sbz_resources:simple_crafting_processor'] = { crafts = 1, power = 5 }, + ['sbz_resources:quick_crafting_processor'] = { crafts = 8, power = 20 }, + ['sbz_resources:fast_crafting_processor'] = { crafts = 32, power = 140 }, + + ['sbz_resources:needlessly_expensive_crafting_processor'] = { crafts = 128, power = 640 }, + ['sbz_resources:omega_quantum_black_hole_whatever_crafting_processor'] = { crafts = 100000, power = 800 }, +} -minetest.register_craft({ - type = "shapeless", - output = "sbz_resources:simple_crafting_processor", - recipe = { "sbz_resources:mosfet", "sbz_resources:simple_circuit", "sbz_meteorites:neutronium" } +core.register_craftitem('sbz_resources:simple_crafting_processor', { + description = 'Simple Crafting Processor', + info_extra = 'Crafts 1 item/s for 5Cj', + inventory_image = 'simple_crafting_processor.png', + stack_max = 1, }) +core.register_craft { + output = 'sbz_resources:simple_crafting_processor', + recipe = { + { 'sbz_resources:matter_blob', 'sbz_resources:emittrium_circuit', 'sbz_resources:matter_blob' }, + { 'sbz_resources:emittrium_circuit', 'sbz_chem:cobalt_ingot', 'sbz_resources:emittrium_circuit' }, + { 'sbz_resources:matter_blob', 'sbz_resources:emittrium_circuit', 'sbz_resources:matter_blob' }, + }, +} - -core.register_craftitem("sbz_resources:quick_crafting_processor", { - description = "Quick Crafting Processor\nCrafts 2 items per second for 25 power.", - inventory_image = "quick_crafting_processor.png", - stack_max = 1 +core.register_craftitem('sbz_resources:fast_crafting_processor', { + description = 'Fast Crafting Processor', + info_extra = 'Crafts 8 items/s for 20Cj', + inventory_image = 'quick_crafting_processor.png', + stack_max = 1, }) -minetest.register_craft({ - type = "shaped", - output = "sbz_resources:quick_crafting_processor", +-- stylua: ignore start +minetest.register_craft { + output = 'sbz_resources:fast_crafting_processor', recipe = { - {"sbz_resources:simple_crafting_processor", "sbz_resources:prediction_circuit", "sbz_resources:simple_crafting_processor"}, - {"sbz_meteorites:neutronium", "sbz_resources:mosfet", "sbz_meteorites:neutronium"}, - {"sbz_resources:simple_crafting_processor", "sbz_meteorites:neutronium", "sbz_resources:simple_crafting_processor"}, - } -}) - + {"sbz_resources:simple_crafting_processor","sbz_resources:reinforced_matter","sbz_resources:simple_crafting_processor",}, + {"sbz_resources:reinforced_matter","sbz_resources:shock_crystal","sbz_resources:reinforced_matter",}, + {"sbz_resources:simple_crafting_processor","sbz_resources:reinforced_matter","sbz_resources:simple_crafting_processor",}, + }, +} +core.register_alias('sbz_resources:quick_crafting_processor', 'sbz_resources:fast_crafting_processor') -core.register_craftitem("sbz_resources:fast_crafting_processor", { - description = "Fast Crafting Processor\nCrafts 4 items per second for 50 power.", - inventory_image = "fast_crafting_processor.png", - stack_max = 1 +core.register_craftitem('sbz_resources:very_fast_crafting_processor', { + description = 'Very Fast Crafting Processor\nCrafts 32 items per second for 140 power.', + inventory_image = 'accelerated_silicon_crafting_processor.png', + stack_max = 1, }) -minetest.register_craft({ - type = "shaped", - output = "sbz_resources:fast_crafting_processor", +minetest.register_craft { + output = 'sbz_resources:very_fast_crafting_processor', recipe = { - {"sbz_resources:quick_crafting_processor", "sbz_resources:emittrium_circuit", "sbz_resources:quick_crafting_processor"}, - {"sbz_resources:mosfet", "sbz_resources:emittrium_circuit", "sbz_resources:mosfet"}, - {"sbz_resources:quick_crafting_processor", "sbz_resources:emittrium_circuit", "sbz_resources:quick_crafting_processor"}, - } -}) - + { 'sbz_resources:fast_crafting_processor', 'sbz_chem:silicon_crystal', 'sbz_resources:fast_crafting_processor' }, + { 'sbz_chem:silicon_crystal', 'sbz_chem:thorium_crystal', 'sbz_chem:silicon_crystal' }, + { 'sbz_resources:fast_crafting_processor', 'sbz_chem:silicon_crystal', 'sbz_resources:fast_crafting_processor' }, + }, +} -core.register_craftitem("sbz_resources:accelerated_silicon_crafting_processor", { - description = "Accelerated Silicon Crafting Processor\nCrafts 8 items per second for 100 power.", - inventory_image = "accelerated_silicon_crafting_processor.png", - stack_max = 1 +core.register_craftitem('sbz_resources:needlessly_expensive_crafting_processor', { + description = 'Needlessly Expensive Crafting Processor', + inventory_image = 'needlessly_expensive_crafting_processor.png', + stack_max = 1, + info_extra = "Crafts 128 items/s and uses 640Cj. You shouldn't need this, this item was made as a joke.", }) -minetest.register_craft({ - type = "shaped", - output = "sbz_resources:accelerated_silicon_crafting_processor", +minetest.register_craft { + type = 'shaped', + output = 'sbz_resources:needlessly_expensive_crafting_processor', recipe = { - {"sbz_resources:fast_crafting_processor", "sbz_chem:silicon_crystal", "sbz_resources:fast_crafting_processor"}, - {"sbz_resources:mosfet", "sbz_resources:mosfet", "sbz_resources:mosfet"}, - {"sbz_resources:fast_crafting_processor", "sbz_meteorites:neutronium", "sbz_resources:fast_crafting_processor"}, - } -}) - + { 'sbz_resources:very_fast_crafting_processor', 'drawers:warpshroom_upgrade', 'sbz_resources:very_fast_crafting_processor' }, + { 'sbz_bio:giant_colorium_sapling', 'sbz_chem:thorium_crystal', 'sbz_bio:giant_colorium_sapling' }, + { 'sbz_resources:very_fast_crafting_processor', 'sbz_chem:xray_off', 'sbz_resources:very_fast_crafting_processor' }, + }, +} -core.register_craftitem("sbz_resources:nuclear_crafting_processor", { - description = "Nuclear Crafting Processor\nCrafts 16 items per second for 175 power.", - inventory_image = "nuclear_crafting_processor.png", - stack_max = 1 +core.register_craftitem('sbz_resources:omega_quantum_black_hole_whatever_crafting_processor', { + description = 'Omega Quantum Black Hole Whatever Crafting Processor', + inventory_image = 'omega_quantum_black_hole_whatever_crafting_processor.png', + info_extra = 'Crafts 100000 items/s, consumes 800Cj.\nThe magic of non-commercial volunteer-run free (as in freedom) games is that you can put in whatever you want.\nThis item is a joke. You should not try to get it.', + stack_max = 1, }) -minetest.register_craft({ - type = "shaped", - output = "sbz_resources:nuclear_crafting_processor", +minetest.register_craft { + output = 'sbz_resources:omega_quantum_black_hole_whatever_crafting_processor', recipe = { - {"sbz_resources:accelerated_silicon_crafting_processor", "sbz_resources:phlogiston_circuit", "sbz_resources:accelerated_silicon_crafting_processor"}, - {"sbz_resources:mosfet", "sbz_resources:mosfet", "sbz_resources:mosfet"}, - {"sbz_resources:accelerated_silicon_crafting_processor", "sbz_meteorites:neutronium", "sbz_resources:accelerated_silicon_crafting_processor"}, - } -}) \ No newline at end of file + { 'sbz_resources:needlessly_expensive_crafting_processor', 'sbz_resources:needlessly_expensive_crafting_processor', 'sbz_resources:needlessly_expensive_crafting_processor', }, + { 'sbz_resources:needlessly_expensive_crafting_processor', 'sbz_resources:needlessly_expensive_crafting_processor', 'sbz_resources:needlessly_expensive_crafting_processor', }, + { 'sbz_resources:needlessly_expensive_crafting_processor', 'sbz_resources:needlessly_expensive_crafting_processor', 'sbz_resources:needlessly_expensive_crafting_processor', }, + }, +} +-- stylua: ignore end + +-- deprecated stuff +core.register_craftitem('sbz_resources:mosfet', { + description = 'Metal-Oxide-Semiconductor Field-Effect Transistor (MOSFET)', + info_extra = 'Deprecated. Throw it away.', + inventory_image = 'mosfet.png', + groups = { not_in_creative_inventory = 1 }, + stack_max = 1, +}) diff --git a/mods/sbz_resources/strange_matter.lua b/mods/sbz_resources/strange_matter.lua index 56fc2c80..7fda5b8b 100644 --- a/mods/sbz_resources/strange_matter.lua +++ b/mods/sbz_resources/strange_matter.lua @@ -1,38 +1,38 @@ -minetest.register_craftitem("sbz_resources:strange_dust", { - description = "Strange Dust", - inventory_image = "strange_dust.png", +minetest.register_craftitem('sbz_resources:strange_dust', { + description = 'Strange Dust', + inventory_image = 'strange_dust.png', }) -minetest.register_node("sbz_resources:strange_blob", { - description = "Strange Blob", - info_extra = "It sure is strange looking... never before seen green color...\n wait did it just- oh no....", - tiles = { "strange_blob.png" }, +minetest.register_node('sbz_resources:strange_blob', { + description = 'Strange Blob', + info_extra = 'It sure is strange looking... never before seen green color...\n wait did it just- oh no....', + tiles = { 'strange_blob.png' }, groups = { matter = 1, antimatter = 1, strange = 1, explody = 100 }, sounds = sbz_api.sounds.strange(), light_source = 14, }) minetest.register_craft { - output = "sbz_resources:strange_blob", + output = 'sbz_resources:strange_blob', recipe = { - { "sbz_resources:strange_dust", "sbz_resources:strange_dust", "sbz_resources:strange_dust", }, - { "sbz_resources:strange_dust", "sbz_resources:strange_dust", "sbz_resources:strange_dust", }, - { "sbz_resources:strange_dust", "sbz_resources:strange_dust", "sbz_resources:strange_dust", }, - } + { 'sbz_resources:strange_dust', 'sbz_resources:strange_dust', 'sbz_resources:strange_dust' }, + { 'sbz_resources:strange_dust', 'sbz_resources:strange_dust', 'sbz_resources:strange_dust' }, + { 'sbz_resources:strange_dust', 'sbz_resources:strange_dust', 'sbz_resources:strange_dust' }, + }, } minetest.register_craft { - output = "sbz_resources:strange_dust 9", - type = "shapeless", - recipe = { "sbz_resources:strange_blob" } + output = 'sbz_resources:strange_dust 9', + type = 'shapeless', + recipe = { 'sbz_resources:strange_blob' }, } -- its been a while since i got to use theese -- - frog -minetest.register_abm({ - label = "Strange blob infecting", - nodenames = { "group:strange" }, - neighbors = { "group:antimatter", "group:matter" }, +minetest.register_abm { + label = 'Strange blob infecting', + nodenames = { 'group:strange' }, + neighbors = { 'group:antimatter', 'group:matter' }, interval = 1, chance = 20, catch_up = false, @@ -40,15 +40,14 @@ minetest.register_abm({ local to_spread = sbz_api.filter_node_neighbors(pos, 1, function(filtering_pos) local filtering_node = minetest.get_node(filtering_pos) local name = filtering_node.name - if minetest.get_item_group(name, "strange") ~= 0 then return end - if minetest.get_item_group(name, "sbz_machine") ~= 0 then return end - if minetest.get_item_group(name, "matter") == 0 and - minetest.get_item_group(name, "antimatter") == 0 then + if minetest.get_item_group(name, 'strange') ~= 0 then return end + if minetest.get_item_group(name, 'sbz_machine') ~= 0 then return end + if minetest.get_item_group(name, 'matter') == 0 and minetest.get_item_group(name, 'antimatter') == 0 then return end - if minetest.get_item_group(name, "no_spread") ~= 0 then return end - if minetest.get_item_group(name, "charged") ~= 0 then return end - if minetest.is_protected(filtering_pos, ".strange_blob_spread") then return end + if minetest.get_item_group(name, 'no_spread') ~= 0 then return end + if minetest.get_item_group(name, 'charged') ~= 0 then return end + if minetest.is_protected(filtering_pos, '.strange_blob_spread') then return end return filtering_pos end) @@ -59,35 +58,33 @@ minetest.register_abm({ local old_meta = minetest.get_meta(v) local meta = minetest.get_meta(v) if next(old_meta:to_table().inventory) == nil then -- you can't serialize userdata - core.swap_node(v, { name = "sbz_resources:strange_blob" }) - meta:set_string("old_meta", minetest.serialize(old_meta:to_table())) - meta:set_string("old_node", minetest.serialize(old_node)) + core.swap_node(v, { name = 'sbz_resources:strange_blob' }) + meta:set_string('old_meta', minetest.serialize(old_meta:to_table())) + meta:set_string('old_node', minetest.serialize(old_node)) end - end -}) - + end, +} local strange_cleaner_radius = 5 local power_per_1_use = 10 local max_wear = power_per_1_use * 200 - -minetest.register_tool("sbz_resources:strange_cleaner", { - description = "Strange Blob Cleaner", +minetest.register_tool('sbz_resources:strange_cleaner', { + description = 'Strange Blob Cleaner', info_extra = { - "Restores what was.... done.... by strange blobs.", - "\"Place\" it into a battery to charge.", + 'Restores what was.... done.... by strange blobs.', + '"Place" it into a battery to charge.', }, - inventory_image = "strange_cleaner.png", + inventory_image = 'strange_cleaner.png', groups = { disable_repair = 1, power_tool = 1 }, on_place = sbz_api.on_place_recharge((max_wear / 65535) * power_per_1_use), powertool_charge = sbz_api.powertool_charge((max_wear / 65535) * power_per_1_use), - wear_represents = "power", + wear_represents = 'power', charge_per_use = power_per_1_use, charge_in_wielders = false, on_use = function(stack, user, pointed) -- boilerplate - if pointed.type ~= "node" then return end + if pointed.type ~= 'node' then return end local target = pointed.under if core.is_protected(target, user:get_player_name()) then return core.record_protection_violation(target, user:get_player_name()) @@ -96,7 +93,6 @@ minetest.register_tool("sbz_resources:strange_cleaner", { -- take away wear - local deferred = {} for x = -strange_cleaner_radius, strange_cleaner_radius do @@ -105,13 +101,11 @@ minetest.register_tool("sbz_resources:strange_cleaner", { local pos = vector.add(target, vector.new(x, y, z)) local node = minetest.get_node(pos) local name = node.name - if minetest.get_item_group(name, "strange") ~= 0 then + if minetest.get_item_group(name, 'strange') ~= 0 then local meta = minetest.get_meta(pos) - local old_node = minetest.deserialize(meta:get_string("old_node")) - local old_meta = minetest.deserialize(meta:get_string("old_meta")) - if old_node == nil then - old_node = { name = "air" } - end + local old_node = minetest.deserialize(meta:get_string 'old_node') + local old_meta = minetest.deserialize(meta:get_string 'old_meta') + if old_node == nil then old_node = { name = 'air' } end deferred[#deferred + 1] = { pos, old_node } meta = minetest.get_meta(pos) if old_meta ~= nil then @@ -125,44 +119,44 @@ minetest.register_tool("sbz_resources:strange_cleaner", { local wear = stack:get_wear() local new_wear = wear + (power_per_1_use * #deferred) - if new_wear >= 65535 then - return - end + if new_wear >= 65535 then return end stack:set_wear(new_wear) for k, v in pairs(deferred) do minetest.swap_node(v[1], v[2]) - if v[3] then - v[3]:from_table(v[4]) - end + if v[3] then v[3]:from_table(v[4]) end end return stack - end + end, }) minetest.register_craft { - output = "sbz_resources:strange_cleaner", + output = 'sbz_resources:strange_cleaner', recipe = { - { "sbz_power:simple_charged_field", "sbz_power:simple_charged_field", "sbz_power:simple_charged_field" }, - { "", "sbz_resources:emittrium_circuit", "" }, - { "", "sbz_resources:matter_blob", "" } - } + { 'sbz_power:simple_charged_field', 'sbz_power:simple_charged_field', 'sbz_power:simple_charged_field' }, + { '', 'sbz_resources:emittrium_circuit', '' }, + { '', 'sbz_resources:matter_blob', '' }, + }, } -minetest.register_node("sbz_resources:stable_strange_blob", unifieddyes.def { - description = "Stabilized Strange Blob", - tiles = { "stable_strange_blob.png" }, - paramtype2 = "color", - paramtype = "light", - groups = { matter = 1, antimatter = 1, strange = 0, charged = 1, explody = 5 }, - light_source = 14, - info_extra = "Now i can enjoy the never-before seen green color with less of the... strange-ness..." -}) +minetest.register_node( + 'sbz_resources:stable_strange_blob', + unifieddyes.def { + description = 'Stabilized Strange Blob', + tiles = { 'stable_strange_blob.png' }, + paramtype2 = 'color', + paramtype = 'light', + groups = { matter = 1, antimatter = 1, strange = 0, charged = 1, explody = 5 }, + light_source = 14, + info_extra = 'Now i can enjoy the never-before seen green color with less of the... strange-ness...', + } +) minetest.register_craft { - output = "sbz_resources:stable_strange_blob", - type = "shapeless", + output = 'sbz_resources:stable_strange_blob', + type = 'shapeless', recipe = { - "sbz_resources:charged_particle", "sbz_resources:strange_blob" - } + 'sbz_resources:charged_particle', + 'sbz_resources:strange_blob', + }, } diff --git a/mods/sbz_resources/textures/emittrium_block.png b/mods/sbz_resources/textures/emittrium_block.png new file mode 100644 index 00000000..0cc0e194 Binary files /dev/null and b/mods/sbz_resources/textures/emittrium_block.png differ diff --git a/mods/sbz_resources/textures/needlessly_expensive_crafting_processor.png b/mods/sbz_resources/textures/needlessly_expensive_crafting_processor.png new file mode 100644 index 00000000..628a233b Binary files /dev/null and b/mods/sbz_resources/textures/needlessly_expensive_crafting_processor.png differ diff --git a/mods/sbz_resources/textures/omega_quantum_black_hole_whatever_crafting_processor.png b/mods/sbz_resources/textures/omega_quantum_black_hole_whatever_crafting_processor.png new file mode 100644 index 00000000..c02f0398 Binary files /dev/null and b/mods/sbz_resources/textures/omega_quantum_black_hole_whatever_crafting_processor.png differ diff --git a/mods/sbz_resources/tools.lua b/mods/sbz_resources/tools.lua index 19459c84..c50586f1 100644 --- a/mods/sbz_resources/tools.lua +++ b/mods/sbz_resources/tools.lua @@ -1,10 +1,8 @@ --- Define a new pickaxe -minetest.register_tool("sbz_resources:matter_annihilator", { - description = "Matter Annihilator", - inventory_image = "matter_annihilator.png", -- Replace with your own image file +minetest.register_craftitem('sbz_resources:matter_annihilator', { + description = 'Matter Annihilator', + inventory_image = 'matter_annihilator.png', groups = { core_drop_multi = 1 }, - -- Tool properties tool_capabilities = { full_punch_interval = 2.5, damage_groups = { matter = 2 }, @@ -16,23 +14,23 @@ minetest.register_tool("sbz_resources:matter_annihilator", { sound = { punch_use = { - name = "block_annihilated", + name = 'block_annihilated', gain = 1, - } + }, }, }) -minetest.register_craft({ - output = "sbz_resources:matter_annihilator", +minetest.register_craft { + output = 'sbz_resources:matter_annihilator', recipe = { - { "", "sbz_resources:antimatter_dust", "" }, - { "sbz_resources:matter_blob", "sbz_resources:charged_particle", "sbz_resources:matter_blob" }, - { "", "sbz_resources:matter_blob", "" } - } -}) + { '', 'sbz_resources:antimatter_dust', '' }, + { 'sbz_resources:matter_blob', 'sbz_resources:charged_particle', 'sbz_resources:matter_blob' }, + { '', 'sbz_resources:matter_blob', '' }, + }, +} -minetest.register_tool("sbz_resources:antimatter_annihilator", { - description = "Antimatter Annihilator", - inventory_image = "antimatter_annihilator.png", -- Replace with your own image file +minetest.register_craftitem('sbz_resources:antimatter_annihilator', { + description = 'Antimatter Annihilator', + inventory_image = 'antimatter_annihilator.png', groups = { core_drop_multi = 1 }, tool_capabilities = { @@ -46,25 +44,24 @@ minetest.register_tool("sbz_resources:antimatter_annihilator", { sound = { punch_use = { - name = "block_annihilated", + name = 'block_annihilated', gain = 1, - } + }, }, }) -minetest.register_craft({ - output = "sbz_resources:antimatter_annihilator", +minetest.register_craft { + output = 'sbz_resources:antimatter_annihilator', recipe = { - { "", "sbz_resources:matter_dust", "" }, - { "sbz_resources:antimatter_blob", "sbz_resources:charged_particle", "sbz_resources:antimatter_blob" }, - { "", "sbz_resources:antimatter_blob", "" } - } -}) + { '', 'sbz_resources:matter_dust', '' }, + { 'sbz_resources:antimatter_blob', 'sbz_resources:charged_particle', 'sbz_resources:antimatter_blob' }, + { '', 'sbz_resources:antimatter_blob', '' }, + }, +} -minetest.register_tool("sbz_resources:robotic_arm", { - description = "Robotic Arm", - inventory_image = "robotic_arm.png", +minetest.register_craftitem('sbz_resources:robotic_arm', { + description = 'Robotic Arm', + inventory_image = 'robotic_arm.png', groups = { core_drop_multi = 2 }, - -- Tool properties tool_capabilities = { full_punch_interval = 0.5, damage_groups = { matter = 1, antimatter = 1 }, @@ -76,29 +73,28 @@ minetest.register_tool("sbz_resources:robotic_arm", { sound = { punch_use = { - name = "block_annihilated", + name = 'block_annihilated', gain = 1, - } + }, }, }) minetest.register_craft { - output = "sbz_resources:robotic_arm", + output = 'sbz_resources:robotic_arm', recipe = { - { "sbz_resources:matter_annihilator", "sbz_chem:iron_ingot", "sbz_resources:matter_annihilator" }, - { "sbz_resources:reinforced_matter", "sbz_resources:emittrium_circuit", "sbz_resources:reinforced_matter" }, - { "sbz_resources:reinforced_matter", "sbz_resources:emittrium_circuit", "sbz_resources:reinforced_matter" } - } + { 'sbz_resources:matter_annihilator', 'sbz_chem:iron_ingot', 'sbz_resources:matter_annihilator' }, + { 'sbz_resources:reinforced_matter', 'sbz_resources:emittrium_circuit', 'sbz_resources:reinforced_matter' }, + { 'sbz_resources:reinforced_matter', 'sbz_resources:emittrium_circuit', 'sbz_resources:reinforced_matter' }, + }, } - local drill_times = { [1] = 1.50 / 2, [2] = 0.30 / 2, [3] = 0.10 / 2 } local drill_max_wear = 500 local drill_power_per_1_use = 10 local tool_caps = { full_punch_interval = 0.1, - damage_groups = { -- yeaa slightly deadly + damage_groups = { matter = 3, antimatter = 3, }, @@ -108,52 +104,50 @@ local tool_caps = { matter = { times = drill_times, --uses = 30, - maxlevel = 4 + maxlevel = 4, }, antimatter = { times = drill_times, --uses = 30, - maxlevel = 4 + maxlevel = 4, }, }, } -minetest.register_tool("sbz_resources:drill", { - description = "Electric Drill", - inventory_image = "drill.png", +minetest.register_tool('sbz_resources:drill', { + description = 'Electric Drill', + inventory_image = 'drill.png', info_extra = { - "Powered by electricity. Wear bar indicates the amount of charge left.", - ("%s uses"):format(drill_max_wear), - "\"Place\" it on a battery to re-charge it." + 'Powered by electricity. Wear bar indicates the amount of charge left.', + ('%s uses'):format(drill_max_wear), + '"Place" it on a battery to re-charge it.', }, groups = { core_drop_multi = 4, disable_repair = 1, power_tool = 1 }, - -- Tool properties tool_capabilities = tool_caps, after_use = function(stack, user, node, digparams) stack:add_wear_by_uses(drill_max_wear + digparams.wear) - if stack:get_wear() >= 65535 then - stack:get_meta():set_tool_capabilities({}) - end + if stack:get_wear() >= 65535 then stack:get_meta():set_tool_capabilities {} end return stack end, - on_place = sbz_api.on_place_recharge((drill_max_wear / 65535) * drill_power_per_1_use, function(stack, user, pointed) - if stack:get_wear() < 65530 then - stack:get_meta():set_tool_capabilities(tool_caps) + on_place = sbz_api.on_place_recharge( + (drill_max_wear / 65535) * drill_power_per_1_use, + function(stack, user, pointed) + if stack:get_wear() < 65530 then stack:get_meta():set_tool_capabilities(tool_caps) end end - end), + ), powertool_charge = sbz_api.powertool_charge((drill_max_wear / 65535) * drill_power_per_1_use), charge_per_use = drill_power_per_1_use, - wear_represents = "power", + wear_represents = 'power', - wear_color = { color_stops = { [0] = "lime" } }, - sound = { punch_use = { name = "drill_dig", } }, + wear_color = { color_stops = { [0] = 'lime' } }, + sound = { punch_use = { name = 'drill_dig' } }, }) minetest.register_craft { recipe = { - { "sbz_chem:titanium_ingot", "sbz_resources:robotic_arm", "sbz_chem:titanium_ingot" }, - { "sbz_chem:titanium_ingot", "sbz_power:battery", "sbz_chem:titanium_ingot" }, - { "sbz_resources:reinforced_matter", "sbz_resources:emittrium_circuit", "sbz_resources:reinforced_matter" } + { 'sbz_chem:titanium_ingot', 'sbz_resources:robotic_arm', 'sbz_chem:titanium_ingot' }, + { 'sbz_chem:titanium_ingot', 'sbz_power:battery', 'sbz_chem:titanium_ingot' }, + { 'sbz_resources:reinforced_matter', 'sbz_resources:emittrium_circuit', 'sbz_resources:reinforced_matter' }, }, - output = "sbz_resources:drill" + output = 'sbz_resources:drill', } diff --git a/mods/sethome b/mods/sethome new file mode 160000 index 00000000..7be37276 --- /dev/null +++ b/mods/sethome @@ -0,0 +1 @@ +Subproject commit 7be372762e94105e13cc410340eaa52b41e7e85f diff --git a/mods/sethome/README.txt b/mods/sethome/README.txt deleted file mode 100644 index 6f0a282b..00000000 --- a/mods/sethome/README.txt +++ /dev/null @@ -1,7 +0,0 @@ -Minetest Game mod: sethome -========================== -See license.txt for license information. - -Authors of source code ----------------------- -sfan5 (MIT) diff --git a/mods/sethome/init.lua b/mods/sethome/init.lua deleted file mode 100644 index 50433832..00000000 --- a/mods/sethome/init.lua +++ /dev/null @@ -1,114 +0,0 @@ --- sethome/init.lua - -sethome = {} - --- Load support for MT game translation. -local S = minetest.get_translator("sethome") - - -local homes_file = minetest.get_worldpath() .. "/homes" -local homepos = {} - -local function loadhomes() - local input = io.open(homes_file, "r") - if not input then - return -- no longer an error - end - - -- Iterate over all stored positions in the format "x y z player" for each line - for pos, name in input:read("*a"):gmatch("(%S+ %S+ %S+)%s([%w_-]+)[\r\n]") do - homepos[name] = minetest.string_to_pos(pos) - end - input:close() -end - -loadhomes() - -sethome.set = function(name, pos) - local player = minetest.get_player_by_name(name) - if not player or not pos then - return false - end - local player_meta = player:get_meta() - player_meta:set_string("sethome:home", minetest.pos_to_string(pos)) - - -- remove `name` from the old storage file - if not homepos[name] then - return true - end - local data = {} - local output = io.open(homes_file, "w") - if output then - homepos[name] = nil - for i, v in pairs(homepos) do - table.insert(data, string.format("%.1f %.1f %.1f %s\n", v.x, v.y, v.z, i)) - end - output:write(table.concat(data)) - io.close(output) - return true - end - return true -- if the file doesn't exist - don't return an error. -end - -sethome.get = function(name) - local player = minetest.get_player_by_name(name) - if not player then - return false, S("This command can only be executed in-game!") - end - local player_meta = player:get_meta() - local pos = minetest.string_to_pos(player_meta:get_string("sethome:home")) - if pos then - return pos - end - - -- fetch old entry from storage table - pos = homepos[name] - if pos then - return vector.new(pos) - else - return nil - end -end - -sethome.go = function(name) - local pos = sethome.get(name) - local player = minetest.get_player_by_name(name) - if player and pos then - player:set_pos(pos) - return true - end - return false -end - -minetest.register_privilege("home", { - description = S("Can use /sethome and /home"), - give_to_singleplayer = false -}) - -minetest.register_chatcommand("home", { - description = S("Teleport you to your home point"), - privs = { interact = true }, - func = function(name) - local player = minetest.get_player_by_name(name) - if not player then - return false, S("This command can only be executed in-game!") - end - if sethome.go(name) then - return true, S("Teleported to home!") - end - return false, S("Set a home using /sethome") - end, -}) - -minetest.register_chatcommand("sethome", { - description = S("Set your home point"), - privs = { home = true }, - func = function(name) - name = name or "" -- fallback to blank name if nil - local player = minetest.get_player_by_name(name) - if player and sethome.set(name, player:get_pos()) then - return true, S("Home set!") - end - return false, S("Player not found!") - end, -}) diff --git a/mods/sethome/license.txt b/mods/sethome/license.txt deleted file mode 100644 index 09f03b09..00000000 --- a/mods/sethome/license.txt +++ /dev/null @@ -1,24 +0,0 @@ -License of source code ----------------------- - -The MIT License (MIT) -Copyright (C) 2014-2016 sfan5 - -Permission is hereby granted, free of charge, to any person obtaining a copy of this -software and associated documentation files (the "Software"), to deal in the Software -without restriction, including without limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of the Software, and to permit -persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or -substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - -For more details: -https://opensource.org/licenses/MIT diff --git a/mods/sethome/locale/sethome.de.tr b/mods/sethome/locale/sethome.de.tr deleted file mode 100644 index c59b26ef..00000000 --- a/mods/sethome/locale/sethome.de.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!=Dieser Befehl kann nur im Spiel ausgeführt werden! -Can use /sethome and /home=Kann /sethome und /home benutzen -Teleport you to your home point=Teleportieren Sie sich zu Ihrem Zuhause-Punkt -Teleported to home!=Nach Hause teleportiert! -Set a home using /sethome=Ein Zuhause mit /sethome setzen -Set your home point=Ihren Zuhause-Punkt setzen -Home set!=Zuhause gesetzt! -Player not found!=Spieler nicht gefunden! diff --git a/mods/sethome/locale/sethome.eo.tr b/mods/sethome/locale/sethome.eo.tr deleted file mode 100644 index 86ef8149..00000000 --- a/mods/sethome/locale/sethome.eo.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!= -Can use /sethome and /home=Povas uzi /sethome kaj /home -Teleport you to your home point=Teletransporti vin al via hejmo -Teleported to home!=Teletransportita al hejmo! -Set a home using /sethome=Fiksi hejmon per /sethome -Set your home point=Fiksi vian hejman punkton -Home set!=Fiksita hejmo! -Player not found!=Ludanto ne troveblas! diff --git a/mods/sethome/locale/sethome.es.tr b/mods/sethome/locale/sethome.es.tr deleted file mode 100644 index 661bc621..00000000 --- a/mods/sethome/locale/sethome.es.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!= -Can use /sethome and /home=Puedes usar /sethome y /home -Teleport you to your home point=Teletranspórtate a tu hogar -Teleported to home!=Ā”Teletransportado a tu hogar! -Set a home using /sethome=Establece tu hogar usando /sethome -Set your home point=Establece el sitio de tu hogar -Home set!=Ā”Hogar establecido! -Player not found!=Ā”Jugador no encontrado! diff --git a/mods/sethome/locale/sethome.fr.tr b/mods/sethome/locale/sethome.fr.tr deleted file mode 100644 index 01345e0e..00000000 --- a/mods/sethome/locale/sethome.fr.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!=Cette commande peut seulement ĆŖtre exĆ©cutĆ©e en jeu ! -Can use /sethome and /home=Peut utiliser /sethome et /home -Teleport you to your home point=Vous tĆ©lĆ©porter Ć  votre domicile -Teleported to home!=TĆ©lĆ©portĆ© Ć  votre domicile ! -Set a home using /sethome=DĆ©finir un domicile en utilisant /sethome -Set your home point=DĆ©finir votre domicile -Home set!=Domicile dĆ©fini ! -Player not found!=Joueur non trouvĆ© ! diff --git a/mods/sethome/locale/sethome.id.tr b/mods/sethome/locale/sethome.id.tr deleted file mode 100644 index f5c68ddb..00000000 --- a/mods/sethome/locale/sethome.id.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!=Perintah ini hanya bisa dijalankan dalam permainan! -Can use /sethome and /home=Boleh gunakan /sethome dan /home -Teleport you to your home point=Teleportasi ke rumah Anda -Teleported to home!=Teleportasi ke rumah! -Set a home using /sethome=Atur letak rumah dengan /sethome -Set your home point=Atur letak rumah -Home set!=Letak rumah diatur! -Player not found!=Pemain tidak ditemukan! diff --git a/mods/sethome/locale/sethome.it.tr b/mods/sethome/locale/sethome.it.tr deleted file mode 100644 index 4f1d7575..00000000 --- a/mods/sethome/locale/sethome.it.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!= -Can use /sethome and /home=Può usare /sethome e /home -Teleport you to your home point=Ti teletrasporta al tuo punto di domicilio -Teleported to home!=Teletrasportato a casa! -Set a home using /sethome=Imposta un domicilio usando /sethome -Set your home point=Imposta il tuo punto di domicilio -Home set!=Domicilio impostato! -Player not found!=Giocatore non trovato! diff --git a/mods/sethome/locale/sethome.ja.tr b/mods/sethome/locale/sethome.ja.tr deleted file mode 100644 index e6523878..00000000 --- a/mods/sethome/locale/sethome.ja.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!= -Can use /sethome and /home=/sethomeと/homećŒä½æćˆć¾ć™ -Teleport you to your home point=ćƒ›ćƒ¼ćƒ åœ°ē‚¹ć«ćƒ†ćƒ¬ćƒćƒ¼ćƒˆć—ć¾ć™ -Teleported to home!=ćƒ›ćƒ¼ćƒ ć«ćƒ†ćƒ¬ćƒćƒ¼ćƒˆļ¼ -Set a home using /sethome=/sethomeć‚’ä½æć£ć¦ćƒ›ćƒ¼ćƒ ć‚’čØ­å®šć—ć¾ć™ -Set your home point=ćƒ›ćƒ¼ćƒ åœ°ē‚¹ć‚’čØ­å®šć—ć¾ć™ -Home set!=ćƒ›ćƒ¼ćƒ åœ°ē‚¹ć‚’ć‚»ćƒƒćƒˆļ¼ -Player not found!=ćƒ—ćƒ¬ćƒ¼ćƒ¤ćƒ¼ćŒč¦‹ć¤ć‹ć‚Šć¾ć›ć‚“ļ¼ diff --git a/mods/sethome/locale/sethome.jbo.tr b/mods/sethome/locale/sethome.jbo.tr deleted file mode 100644 index 2fe7bf0d..00000000 --- a/mods/sethome/locale/sethome.jbo.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!= -Can use /sethome and /home=kakne lo nu pilno lo me zoi gy./sethome.gy. ku .e lo me zoi gy./home.gy. -Teleport you to your home point=sukmu'u lo do zdani mokca -Teleported to home!=puba'o sukmu'u lo zdani -Set a home using /sethome=ko tcimi'e fi lo zdani sepi'o lo me zoi gy./sethome.gy. -Set your home point=tcimi'e fi lo do zdani mokca -Home set!=puba'o tcimi'e fi lo zdani -Player not found!=lo kelci na te facki diff --git a/mods/sethome/locale/sethome.ms.tr b/mods/sethome/locale/sethome.ms.tr deleted file mode 100644 index 09721fef..00000000 --- a/mods/sethome/locale/sethome.ms.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!=Perintah ini hanya boleh dijalankan dalam permainan! -Can use /sethome and /home=Boleh guna /sethome dan /home -Teleport you to your home point=Teleportasikan anda ke titik rumah anda -Teleported to home!=Diteleportasikan ke rumah! -Set a home using /sethome=Tetapkan rumah menggunakan /sethome -Set your home point=Tetapkan titik rumah anda -Home set!=Rumah ditetapkan! -Player not found!=Pemain tidak dijumpai! diff --git a/mods/sethome/locale/sethome.pl.tr b/mods/sethome/locale/sethome.pl.tr deleted file mode 100644 index b45cb463..00000000 --- a/mods/sethome/locale/sethome.pl.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!= -Can use /sethome and /home=Może używać /sethome i /home -Teleport you to your home point=Teleportuj się do swojego punktu domowego -Teleported to home!=Teleportowano do punktu domowego -Set a home using /sethome=Ustaw punkt domowy używając /sethome -Set your home point=Ustaw swój punkt domowy -Home set!=Punkt domowy ustawiony! -Player not found!=Gracz nie odnaleziony! diff --git a/mods/sethome/locale/sethome.pt_BR.tr b/mods/sethome/locale/sethome.pt_BR.tr deleted file mode 100644 index 6abd416d..00000000 --- a/mods/sethome/locale/sethome.pt_BR.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!= -Can use /sethome and /home=Pode usar /sethome e /home -Teleport you to your home point=TeletransportĆ”-lo para seu ponto de origem -Teleported to home!=Teletransportado para o ponto de origem! -Set a home using /sethome=Defina um ponto de origem usando /sethome -Set your home point=Define seu ponto de origem -Home set!=Ponto de origem definido! -Player not found!=Jogador nĆ£o encontrado! diff --git a/mods/sethome/locale/sethome.ru.tr b/mods/sethome/locale/sethome.ru.tr deleted file mode 100644 index d68cf6e4..00000000 --- a/mods/sethome/locale/sethome.ru.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!=Эта команГа может Š±Ń‹Ń‚ŃŒ использована Ń‚Š¾Š»ŃŒŠŗŠ¾ в игре! -Can use /sethome and /home=Š’Š¾Š·Š¼Š¾Š¶Š½Š¾ŃŃ‚ŃŒ ŠøŃŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Ń‚ŃŒ /sethome Šø /home -Teleport you to your home point=Š’Ń‹ Ń‚ŠµŠ»ŠµŠæŠ¾Ń€Ń‚ŠøŃ€ŃƒŠµŃ‚ŠµŃŃŒ в ŃŠ²Š¾ŃŽ Š“Š¾Š¼Š°ŃˆŠ½ŃŽŃŽ Ń‚Š¾Ń‡ŠŗŃƒ -Teleported to home!=Š’Ń‹ Ń‚ŠµŠ»ŠµŠæŠ¾Ń€Ń‚ŠøŃ€Š¾Š²Š°Š»ŠøŃŃŒ Гомой! -Set a home using /sethome=Установите Š“Š¾Š¼Š°ŃˆŠ½ŃŽŃŽ Ń‚Š¾Ń‡ŠŗŃƒ, ŠøŃŠæŠ¾Š»ŃŒŠ·ŃƒŃ /sethome -Set your home point=Установите вашу Š“Š¾Š¼Š°ŃˆŠ½ŃŽŃŽ Ń‚Š¾Ń‡ŠŗŃƒ -Home set!=Š”Š¾Š¼Š°ŃˆŠ½ŃŃ точка ŃƒŃŃ‚Š°Š½Š¾Š²Š»ŠµŠ½Š°! -Player not found!=Š˜Š³Ń€Š¾Šŗ не найГен! diff --git a/mods/sethome/locale/sethome.sk.tr b/mods/sethome/locale/sethome.sk.tr deleted file mode 100644 index c0e6cee8..00000000 --- a/mods/sethome/locale/sethome.sk.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!= -Can use /sethome and /home=MÓžeÅ” použivaÅ„ /sethome a /home -Teleport you to your home point=Teleportuj sa domov -Teleported to home!=Teleportovaný domov! -Set a home using /sethome=Nastav si domov použitĆ­m /sethome -Set your home point=NastaviÅ„ si domov -Home set!=Domov nastavený! -Player not found!=HrĆ”Ä nenĆ”jdený! diff --git a/mods/sethome/locale/sethome.sv.tr b/mods/sethome/locale/sethome.sv.tr deleted file mode 100644 index 4e100b8b..00000000 --- a/mods/sethome/locale/sethome.sv.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!= -Can use /sethome and /home=Kan anvƤnda /sethome och /home -Teleport you to your home point=Teleportera dig till din hempunkt -Teleported to home!=Teleporterad hem! -Set a home using /sethome=StƤll in ett hem med /sethome -Set your home point=StƤll in din hempunkt -Home set!=Hem instƤllt! -Player not found!=Spelare finns inte! diff --git a/mods/sethome/locale/sethome.uk.tr b/mods/sethome/locale/sethome.uk.tr deleted file mode 100644 index f17ac777..00000000 --- a/mods/sethome/locale/sethome.uk.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!=Š¦Ń команГа може Š±ŃƒŃ‚Šø виконана Ń‚Ń–Š»ŃŒŠŗŠø у грі! -Can use /sethome and /home=ŠœŠ¾Š¶Š»ŠøŠ²Ń–ŃŃ‚ŃŒ Š²ŠøŠŗŠ¾Ń€ŠøŃŃ‚Š°Š½Š½Ń /sethome та /home -Teleport you to your home point=Š¢ŠµŠ»ŠµŠæŠ¾Ń€Ń‚ŃƒŠ²Š°Ń‚ŠøŃŃ Го Š“Š¾Š¼Š°ŃˆŠ½ŃŒŠ¾Ń— точки -Teleported to home!=Телепортовано ГоГому! -Set a home using /sethome=Š’ŃŃ‚Š°Š½Š¾Š²Ń–Ń‚ŃŒ Š“Š¾Š¼Š°ŃˆŠ½ŃŽ Ń‚Š¾Ń‡ŠŗŃƒ, Š²ŠøŠŗŠ¾Ń€ŠøŃŃ‚Š¾Š²ŃƒŃŽŃ‡Šø /sethome -Set your home point=Встановити Š“Š¾Š¼Š°ŃˆŠ½ŃŽ Ń‚Š¾Ń‡ŠŗŃƒ -Home set!=Š”Š¾Š¼Š°ŃˆŠ½ŃŽ Ń‚Š¾Ń‡ŠŗŃƒ встановлено! -Player not found!=Š“Ń€Š°Š²Ń†Ń не знайГено! diff --git a/mods/sethome/locale/sethome.zh_CN.tr b/mods/sethome/locale/sethome.zh_CN.tr deleted file mode 100644 index 6e05576b..00000000 --- a/mods/sethome/locale/sethome.zh_CN.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!=čÆ„ęŒ‡ä»¤åŖčƒ½åœØęøøęˆå†…ä½æē”Øļ¼ -Can use /sethome and /home=åÆä»„ä½æē”Ø/sethome和/home -Teleport you to your home point=å°†ę‚Øä¼ é€åˆ°å®¶ -Teleported to home!=å·²ä¼ é€åˆ°å®¶ļ¼ -Set a home using /sethome=使用/sethome设定家 -Set your home point=č®¾å®šę‚Øå®¶ēš„åœ°ē‚¹ -Home set!=已设定家! -Player not found!=ęœŖę‰¾åˆ°ēŽ©å®¶ļ¼ diff --git a/mods/sethome/locale/sethome.zh_TW.tr b/mods/sethome/locale/sethome.zh_TW.tr deleted file mode 100644 index c5e455cc..00000000 --- a/mods/sethome/locale/sethome.zh_TW.tr +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!=ę­¤ęŒ‡ä»¤åƒ…čƒ½åœØęøøęˆ²å†…ä½æē”Øļ¼ -Can use /sethome and /home=åÆä»„ä½æē”Ø/sethome和/home -Teleport you to your home point=å‚³é€ę‚Øåˆ°ę‚Øå®¶ēš„åœ°é»ž -Teleported to home!=å·²å‚³é€åˆ°å®¶ļ¼ -Set a home using /sethome=使用/sethome設定家 -Set your home point=čØ­å®šę‚Øå®¶ēš„åœ°é»ž -Home set!=已設定家! -Player not found!=ęœŖę‰¾åˆ°ēŽ©å®¶ļ¼ diff --git a/mods/sethome/locale/template.txt b/mods/sethome/locale/template.txt deleted file mode 100644 index f91c719c..00000000 --- a/mods/sethome/locale/template.txt +++ /dev/null @@ -1,9 +0,0 @@ -# textdomain: sethome -This command can only be executed in-game!= -Can use /sethome and /home= -Teleport you to your home point= -Teleported to home!= -Set a home using /sethome= -Set your home point= -Home set!= -Player not found!= diff --git a/mods/sethome/mod.conf b/mods/sethome/mod.conf deleted file mode 100644 index 00799251..00000000 --- a/mods/sethome/mod.conf +++ /dev/null @@ -1,2 +0,0 @@ -name = sethome -description = Minetest Game mod: sethome diff --git a/mods/stubes b/mods/stubes new file mode 160000 index 00000000..46fb8664 --- /dev/null +++ b/mods/stubes @@ -0,0 +1 @@ +Subproject commit 46fb86643f91b89e633082fbff876be90d2fd4ab diff --git a/mods/unified_inventory_plus b/mods/unified_inventory_plus new file mode 160000 index 00000000..836ebf5a --- /dev/null +++ b/mods/unified_inventory_plus @@ -0,0 +1 @@ +Subproject commit 836ebf5a172e752d5f17865874f5b04d60da3319 diff --git a/mods/unified_inventory_plus/.github/workflows/luacheck.yml b/mods/unified_inventory_plus/.github/workflows/luacheck.yml deleted file mode 100644 index 18088bce..00000000 --- a/mods/unified_inventory_plus/.github/workflows/luacheck.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: luacheck -on: [push, pull_request] -jobs: - luacheck: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@main - - name: Luacheck - uses: lunarmodules/luacheck@master diff --git a/mods/unified_inventory_plus/LICENSE b/mods/unified_inventory_plus/LICENSE deleted file mode 100644 index b6ff27cc..00000000 --- a/mods/unified_inventory_plus/LICENSE +++ /dev/null @@ -1,168 +0,0 @@ -this license is for the code. -any non-code media included in this repository is covered by the contents of MEDIA_LICENSE.txt. - - GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - - This version of the GNU Lesser General Public License incorporates -the terms and conditions of version 3 of the GNU General Public -License, supplemented by the additional permissions listed below. - - 0. Additional Definitions. - - As used herein, "this License" refers to version 3 of the GNU Lesser -General Public License, and the "GNU GPL" refers to version 3 of the GNU -General Public License. - - "The Library" refers to a covered work governed by this License, -other than an Application or a Combined Work as defined below. - - An "Application" is any work that makes use of an interface provided -by the Library, but which is not otherwise based on the Library. -Defining a subclass of a class defined by the Library is deemed a mode -of using an interface provided by the Library. - - A "Combined Work" is a work produced by combining or linking an -Application with the Library. The particular version of the Library -with which the Combined Work was made is also called the "Linked -Version". - - The "Minimal Corresponding Source" for a Combined Work means the -Corresponding Source for the Combined Work, excluding any source code -for portions of the Combined Work that, considered in isolation, are -based on the Application, and not on the Linked Version. - - The "Corresponding Application Code" for a Combined Work means the -object code and/or source code for the Application, including any data -and utility programs needed for reproducing the Combined Work from the -Application, but excluding the System Libraries of the Combined Work. - - 1. Exception to Section 3 of the GNU GPL. - - You may convey a covered work under sections 3 and 4 of this License -without being bound by section 3 of the GNU GPL. - - 2. Conveying Modified Versions. - - If you modify a copy of the Library, and, in your modifications, a -facility refers to a function or data to be supplied by an Application -that uses the facility (other than as an argument passed when the -facility is invoked), then you may convey a copy of the modified -version: - - a) under this License, provided that you make a good faith effort to - ensure that, in the event an Application does not supply the - function or data, the facility still operates, and performs - whatever part of its purpose remains meaningful, or - - b) under the GNU GPL, with none of the additional permissions of - this License applicable to that copy. - - 3. Object Code Incorporating Material from Library Header Files. - - The object code form of an Application may incorporate material from -a header file that is part of the Library. You may convey such object -code under terms of your choice, provided that, if the incorporated -material is not limited to numerical parameters, data structure -layouts and accessors, or small macros, inline functions and templates -(ten or fewer lines in length), you do both of the following: - - a) Give prominent notice with each copy of the object code that the - Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the object code with a copy of the GNU GPL and this license - document. - - 4. Combined Works. - - You may convey a Combined Work under terms of your choice that, -taken together, effectively do not restrict modification of the -portions of the Library contained in the Combined Work and reverse -engineering for debugging such modifications, if you also do each of -the following: - - a) Give prominent notice with each copy of the Combined Work that - the Library is used in it and that the Library and its use are - covered by this License. - - b) Accompany the Combined Work with a copy of the GNU GPL and this license - document. - - c) For a Combined Work that displays copyright notices during - execution, include the copyright notice for the Library among - these notices, as well as a reference directing the user to the - copies of the GNU GPL and this license document. - - d) Do one of the following: - - 0) Convey the Minimal Corresponding Source under the terms of this - License, and the Corresponding Application Code in a form - suitable for, and under terms that permit, the user to - recombine or relink the Application with a modified version of - the Linked Version to produce a modified Combined Work, in the - manner specified by section 6 of the GNU GPL for conveying - Corresponding Source. - - 1) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (a) uses at run time - a copy of the Library already present on the user's computer - system, and (b) will operate properly with a modified version - of the Library that is interface-compatible with the Linked - Version. - - e) Provide Installation Information, but only if you would otherwise - be required to provide such information under section 6 of the - GNU GPL, and only to the extent that such information is - necessary to install and execute a modified version of the - Combined Work produced by recombining or relinking the - Application with a modified version of the Linked Version. (If - you use option 4d0, the Installation Information must accompany - the Minimal Corresponding Source and Corresponding Application - Code. If you use option 4d1, you must provide the Installation - Information in the manner specified by section 6 of the GNU GPL - for conveying Corresponding Source.) - - 5. Combined Libraries. - - You may place library facilities that are a work based on the -Library side by side in a single library together with other library -facilities that are not Applications and are not covered by this -License, and convey such a combined library under terms of your -choice, if you do both of the following: - - a) Accompany the combined library with a copy of the same work based - on the Library, uncombined with any other library facilities, - conveyed under the terms of this License. - - b) Give prominent notice with the combined library that part of it - is a work based on the Library, and explaining where to find the - accompanying uncombined form of the same work. - - 6. Revised Versions of the GNU Lesser General Public License. - - The Free Software Foundation may publish revised and/or new versions -of the GNU Lesser General Public License from time to time. Such new -versions will be similar in spirit to the present version, but may -differ in detail to address new problems or concerns. - - Each version is given a distinguishing version number. If the -Library as you received it specifies that a certain numbered version -of the GNU Lesser General Public License "or any later version" -applies to it, you have the option of following the terms and -conditions either of that published version or of any later version -published by the Free Software Foundation. If the Library as you -received it does not specify a version number of the GNU Lesser -General Public License, you may choose any version of the GNU Lesser -General Public License ever published by the Free Software Foundation. - - If the Library as you received it specifies that a proxy can decide -whether future versions of the GNU Lesser General Public License shall -apply, that proxy's public statement of acceptance of any version is -permanent authorization for you to choose that version for the -Library. diff --git a/mods/unified_inventory_plus/MEDIA_LICENSE.txt b/mods/unified_inventory_plus/MEDIA_LICENSE.txt deleted file mode 100644 index 0e259d42..00000000 --- a/mods/unified_inventory_plus/MEDIA_LICENSE.txt +++ /dev/null @@ -1,121 +0,0 @@ -Creative Commons Legal Code - -CC0 1.0 Universal - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. diff --git a/mods/unified_inventory_plus/README.md b/mods/unified_inventory_plus/README.md deleted file mode 100644 index 0f067b55..00000000 --- a/mods/unified_inventory_plus/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Unified Inventory Plus -Unified Inventory Plus extends Unified Inventory. -It adds 3 button types to the craft page: -- Craft All: crafting a stack and putting the result in the inventory -- Patterns buttons: reorganizing items in the craft inventory following usual patterns -- Rotation button: rotating the craft inventory content -- Clear button: clear the craft inventory content - -In `init.lua`, you can easily: -Disable the functionalities you don't want -Disable and reorder patterns - -Fully compatible with Skyblock - -## Settings -Features can be toggled with the following settings (true per default): -* `unified_inventory_plus.enable_craft_all` -* `unified_inventory_plus.enable_craft_organize` -* `unified_inventory_plus.enable_craft_rotate` -* `unified_inventory_plus.enable_craft_clear` - -## Dependencies -- [unified_inventory](https://github.com/minetest-mods/unified_inventory) -- [fakelib](https://content.minetest.net/packages/OgelGames/fakelib/) -#### Optional dependencies -- [stamina](https://github.com/minetest-mods/stamina) - - -## License -Refer to Unified Inventory for original medias copyright. - -Copyright (C) 2017 Bousket -Unified Inventory Plus code is licensed under the CC0 -https://creativecommons.org/publicdomain/zero/1.0/ - -Copyright (C) 2022 fluxionary -Code: LGPL -Media: CC0 - -## Authors -Bousket - Main mod. -shivajiva101 - Skyblock Integration. -fluxionary - Stamina mod integration, rewrite. -NatureFreshMilk. -Panquesito7. diff --git a/mods/unified_inventory_plus/craft_all.lua b/mods/unified_inventory_plus/craft_all.lua deleted file mode 100644 index 7a3beb63..00000000 --- a/mods/unified_inventory_plus/craft_all.lua +++ /dev/null @@ -1,150 +0,0 @@ -local ui = unified_inventory -local uip = unified_inventory_plus -local has = uip.has -local settings = uip.settings -local S = uip.S -local F = minetest.formspec_escape - --- Backup to inject code -uip.craft_all = ui.pages["craft"].get_formspec - -ui.pages["craft"] = { - get_formspec = function(player, perplayer_formspec) - local formspec = uip.craft_all(player, perplayer_formspec).formspec - formspec = formspec .. - ("image[%f,%f;%f,%f;ui_crafting_long_arrow.png]"):format( - perplayer_formspec.craft_arrow_x, - perplayer_formspec.craft_y, - ui.imgscale, - ui.imgscale * 3) .. - ("button[%f,%f;%f,%f;craft_craftall;%s]"):format( - perplayer_formspec.craft_arrow_x + 0.23, - perplayer_formspec.craft_y + 1.50, - perplayer_formspec.btn_size, - perplayer_formspec.btn_size, - F(S("All")) - ) - return { formspec = formspec } - end, -} - --- make sure the width is right -local function infer_width(list, expected) - if not expected or expected:is_empty() then - return - end - local width - for i = 1, 3 do - local output, _ = minetest.get_craft_result({ method = "normal", width = i, items = list }) - if output.item:to_string() == expected:to_string() then - width = i - break - end - end - if not width then - uip.log("warning", "Can't infer recipe width for %s", expected:to_string()) - end - return width -end - --- Craft max possible items and put the result in the main inventory -local function craft_craftall(player) - local player_name = player:get_player_name() - local player_inv = player:get_inventory() - local craft_list = player_inv:get_list("craft") - local expected_result = player_inv:get_stack("craftpreview", 1) - local craft_width = infer_width(craft_list, expected_result) - if not craft_width then - return - end - - local num_crafted = 0 - -- don't modify player's inventory until we're done, in case something goes wrong (e.g. crash) - -- use a fake inventory instead of a detached inventory, because detached inventory actions all result in packets - -- sent to the player. - local tmp_inv = fakelib.create_inventory() - tmp_inv:set_list("main", player_inv:get_list("main")) - tmp_inv:set_list("craft", craft_list) - - while true do - if ( - has.stamina and - stamina.get_saturation and - stamina.get_saturation(player) <= settings.craft_all_min_saturation - ) then - minetest.chat_send_player(player_name, S("You are too hungry to use Craft All at this time.")) - break - end - - -- note that get_craft_result can be *very* slow, until minetest 5.7.0 is released. - -- see https://github.com/minetest/minetest/issues/13231 - local output, decremented_input = minetest.get_craft_result({ - method = "normal", - width = craft_width, - items = craft_list, - }) - - if output.item:get_name() ~= expected_result:get_name() then - -- the recipe changed, so we've run out of something. stop processing. - break - end - - -- minetest.on_craft expects to see the decremented input list. - tmp_inv:set_list("craft", decremented_input.items) - - -- invoke callbacks, for compatibility w/ stamina, skyblock, moretrees, etc. - -- note that this is an *undocumented* handler of the minetest lua API, and possibly is subject to change - -- in the future. - output.item = minetest.on_craft(output.item, player, craft_list, tmp_inv) - - -- track items added to the inventory, in case we need to remove them later - local added = {} - - if tmp_inv:room_for_item("main", output.item) then - tmp_inv:add_item("main", output.item) -- should be no remainder, ignore it - table.insert(added, output.item) - else - -- no room for the output item, stop - break - end - - -- we now try to add all replacements. - local all_added = true - for _, replacement_stk in ipairs(output.replacements) do - if tmp_inv:room_for_item("main", replacement_stk) then - tmp_inv:add_item("main", replacement_stk) -- should be no remainder, ignore it - table.insert(added, replacement_stk) - else - all_added = false - break - end - end - - if not all_added then - -- if we failed to add all the replacements, remove what we've added, and abort - for _, stk in ipairs(added) do - tmp_inv:remove_item("main", stk) -- should be no remainder, ignore it - end - break - end - - -- the craft list can be modified by the callbacks, so re-load it - craft_list = tmp_inv:get_list("craft") - - num_crafted = num_crafted + 1 - end - - player_inv:set_list("craft", craft_list) - player_inv:set_list("main", tmp_inv:get_list("main")) - - uip.log("action", "player %s crafts %s %i", player_name, expected_result:to_string(), num_crafted) -end - -minetest.register_on_player_receive_fields(function(player, formname, fields) - for k, _ in pairs(fields) do - if k:match("craft_craftall") then - craft_craftall(player) - return - end - end -end) diff --git a/mods/unified_inventory_plus/craft_clear.lua b/mods/unified_inventory_plus/craft_clear.lua deleted file mode 100644 index 572343c2..00000000 --- a/mods/unified_inventory_plus/craft_clear.lua +++ /dev/null @@ -1,43 +0,0 @@ --- Clear items in the craft inventory -local ui = unified_inventory -local uip = unified_inventory_plus - --- Backup to inject code -uip.craft_clear = ui.pages["craft"].get_formspec - -ui.pages["craft"] = { - get_formspec = function(player, perplayer_formspec) - local formspec = uip.craft_clear(player, perplayer_formspec).formspec - formspec = formspec .. - ("image_button[%f,%f;%f,%f;pattern_clear.png;craft_clear;]"):format( - perplayer_formspec.craft_x - perplayer_formspec.btn_spc, - perplayer_formspec.craft_y + ui.imgscale, - perplayer_formspec.btn_size, - perplayer_formspec.btn_size - ) - return { formspec = formspec } - end, -} - --- Return items from the craft inventory to the player's inventory -local function craft_clear(player) - local player_inv = player:get_inventory() - local craft_list = player_inv:get_list("craft") - - for i, stk in ipairs(craft_list) do - if player_inv:room_for_item("main", stk) then - player_inv:add_item("main", stk) - craft_list[i]:clear() - end - end - - player_inv:set_list("craft", craft_list) -end - -minetest.register_on_player_receive_fields(function(player, formname, fields) - for k, _ in pairs(fields) do - if k:match("craft_clear") then - craft_clear(player) - end - end -end) diff --git a/mods/unified_inventory_plus/craft_organize.lua b/mods/unified_inventory_plus/craft_organize.lua deleted file mode 100644 index 40bf7753..00000000 --- a/mods/unified_inventory_plus/craft_organize.lua +++ /dev/null @@ -1,170 +0,0 @@ --- Organize items in the craft inventory following a pattern: -local ui = unified_inventory -local uip = unified_inventory_plus - -local default_stack_max = tonumber(minetest.settings:get("default_stack_max")) or 99 - --- Backup to inject code -uip.craft_organize = ui.pages["craft"].get_formspec - -ui.pages["craft"] = { - get_formspec = function(player, perplayer_formspec) - local formspec = uip.craft_organize(player, perplayer_formspec).formspec - local btnsz = ui.imgscale / 3 - local btnspc = ui.imgscale / 2 - - if perplayer_formspec.pagecols == 4 then - -- UI is in lite mode. - for i, v in ipairs(uip.craft_patterns) do - formspec = formspec .. - ("image_button[%f,%f;%f,%f;%s;craft_organize_%i;]"):format( - perplayer_formspec.craft_x + btnspc * (i - 1), - perplayer_formspec.craft_y + 0.1 - btnspc, - btnsz, - btnsz, - v.ico, - i - ) - end - else - for i, v in ipairs(uip.craft_patterns) do - formspec = formspec .. - ("image_button[%f,%f;%f,%f;%s;craft_organize_%i;]"):format( - perplayer_formspec.craft_x + btnspc * ((i - 1) % 6) + 0.1, - perplayer_formspec.craft_y + 0.22 - (math.ceil(i / 6)) * btnspc, - btnsz, - btnsz, - v.ico, - i - ) - end - end - return { formspec = formspec } - end, -} - -local function get_pattern_id(fields) - for k, _ in pairs(fields) do - local pattern_id = tonumber(k:match("craft_organize_(.*)")) - if pattern_id then - return pattern_id - end - end -end - -local function get_single_item(craft_list) - for _, stk in ipairs(craft_list) do - if not stk:is_empty() then - local item = ItemStack(stk) - item:set_count(1) - return item - end - end -end - -local function all_identical(craft_list) - local single_strings = {} - for _, stk in ipairs(craft_list) do - if not stk:is_empty() then - local item = ItemStack(stk) - item:set_count(1) - table.insert(single_strings, item:to_string()) - end - end - if #single_strings == 0 then return false end - local first = single_strings[1] - for i = 2, #single_strings do - if first ~= single_strings[i] then - return false - end - end - return true -end - -local function count_items(craft_list) - local count = 0 - for _, stk in ipairs(craft_list) do - count = count + stk:get_count() - end - return count -end - --- Organize items in the craft inventory following a pattern -local function craft_organize(player, fields) - local player_name = player:get_player_name() - - local pattern_id = get_pattern_id(fields) - if not pattern_id or not uip.craft_patterns[pattern_id] then - minetest.chat_send_player(player_name, "Unexpected pattern!?") - return - end - local pattern = uip.craft_patterns[pattern_id].pattern - local pattern_size = #pattern - - local player_inv = player:get_inventory() - local craft_list = player_inv:get_list("craft") - - local single_item = get_single_item(craft_list) - if not single_item then - -- craft inv is empty - minetest.chat_send_player(player_name, "Inventory empty.") - return - end - - if not all_identical(craft_list) then - minetest.chat_send_player(player_name, "You can only organize one type of item.") - return - end - - local itemname = single_item:get_name() - local def = minetest.registered_items[itemname] - if not def then - minetest.chat_send_player(player_name, "You can't organize an unknown item.") - return - end - - local stacksize = def.stack_max or default_stack_max - local num_items = count_items(craft_list) - - if num_items > stacksize * pattern_size then - minetest.chat_send_player(player_name, "Too many items to stack in that pattern.") - return - end - - if num_items < pattern_size then - minetest.chat_send_player(player_name, "Not enough items.") - return - end - - local new_stack_size = math.floor(num_items / pattern_size) - local remainder = num_items % pattern_size - - local new_craft_list = { - ItemStack(), ItemStack(), ItemStack(), - ItemStack(), ItemStack(), ItemStack(), - ItemStack(), ItemStack(), ItemStack(), - } - - -- because remainder is strictly less than pattern_size, this should always finish w/ remainder = 0 - for _, index in ipairs(pattern) do - local new_item = ItemStack(single_item) - if remainder > 0 then - new_item:set_count(new_stack_size + 1) - remainder = remainder - 1 - else - new_item:set_count(new_stack_size) - end - new_craft_list[index] = new_item - end - - player_inv:set_list("craft", new_craft_list) -end - -minetest.register_on_player_receive_fields(function(player, formname, fields) - for k, _ in pairs(fields) do - if k:match("craft_organize_") then - craft_organize(player, fields) - return - end - end -end) diff --git a/mods/unified_inventory_plus/craft_rotate.lua b/mods/unified_inventory_plus/craft_rotate.lua deleted file mode 100644 index de75bf91..00000000 --- a/mods/unified_inventory_plus/craft_rotate.lua +++ /dev/null @@ -1,52 +0,0 @@ --- Rotate items in the craft inventory -local ui = unified_inventory -local uip = unified_inventory_plus - --- Backup to inject code -uip.craft_rotate = ui.pages["craft"].get_formspec - -ui.pages["craft"] = { - get_formspec = function(player, perplayer_formspec) - local formspec = uip.craft_rotate(player, perplayer_formspec).formspec - formspec = formspec .. - ("image_button[%f,%f;%f,%f;pattern_rotate.png;craft_rotate;]"):format( - perplayer_formspec.craft_x - perplayer_formspec.btn_spc, - perplayer_formspec.craft_y, - perplayer_formspec.btn_size, - perplayer_formspec.btn_size - ) - return { formspec = formspec } - end, -} - - --- Rotate items in the craft inventory -local function craft_rotate_cw(player) - local player_inv = player:get_inventory() - local craft_list = player_inv:get_list("craft") - - -- Rotate corners - local stack = craft_list[1] - craft_list[1] = craft_list[7] - craft_list[7] = craft_list[9] - craft_list[9] = craft_list[3] - craft_list[3] = stack - - -- Rotate middle ones - stack = craft_list[2] - craft_list[2] = craft_list[4] - craft_list[4] = craft_list[8] - craft_list[8] = craft_list[6] - craft_list[6] = stack - - player_inv:set_list("craft", craft_list) -end - -minetest.register_on_player_receive_fields(function(player, formname, fields) - --if not formname:match("craft") then return end - for k, _ in pairs(fields) do - if k:match("craft_rotate") then - craft_rotate_cw(player) - end - end -end) diff --git a/mods/unified_inventory_plus/init.lua b/mods/unified_inventory_plus/init.lua deleted file mode 100644 index 409250bd..00000000 --- a/mods/unified_inventory_plus/init.lua +++ /dev/null @@ -1,57 +0,0 @@ --- Unified Inventory Plus for Minetest -local modname = minetest.get_current_modname() -local modpath = minetest.get_modpath(modname) - -unified_inventory_plus = { - -- Patterns: (Comment unwanted ones & reorder as you wish: buttons are then placed left to right, - -- then up, 6 on a row) pattern field contains indexes to fill in the craft inventory according - -- to the scheme: (then others for non creatives) - -- - -- 1 2 3 - -- 4 5 6 - -- 7 8 9 - craft_patterns = { - { ico = "pattern_1.png", pattern = { 1 } }, - { ico = "pattern_4.png", pattern = { 1, 2, 4, 5 } }, - { ico = "pattern_9.png", pattern = { 1, 2, 3, 4, 5, 6, 7, 8, 9 } }, - { ico = "pattern_3.png", pattern = { 1, 2, 3 } }, - { ico = "pattern_3b.png", pattern = { 1, 4, 7 } }, - { ico = "pattern_5.png", pattern = { 1, 3, 5, 7, 9 } }, - { ico = "pattern_6.png", pattern = { 1, 4, 5, 7, 8, 9 } }, - { ico = "pattern_6b.png", pattern = { 1, 2, 3, 4, 5, 6 } }, - { ico = "pattern_6c.png", pattern = { 1, 3, 4, 6, 7, 9 } }, - { ico = "pattern_7.png", pattern = { 1, 2, 3, 4, 5, 6, 8 } }, - { ico = "pattern_8.png", pattern = { 1, 2, 3, 4, 6, 7, 8, 9 } }, - }, - - has = { - stamina = minetest.global_exists("stamina"), - }, - - log = function(level, message_fmt, ...) - minetest.log(level, "[unified_inventory_plus] " .. message_fmt:format(...)) - end, - - S = minetest.get_translator("unified_inventory"), -} - -local uip = unified_inventory_plus - -dofile(modpath .. "/settings.lua") - --- Functionalities are independants. -if uip.settings.enable_craft_all then - dofile(modpath .. "/craft_all.lua") -end - -if uip.settings.enable_craft_organize then - dofile(modpath .. "/craft_organize.lua") -end - -if uip.settings.enable_craft_rotate then - dofile(modpath .. "/craft_rotate.lua") -end - -if uip.settings.enable_craft_clear then - dofile(modpath .. "/craft_clear.lua") -end diff --git a/mods/unified_inventory_plus/mod.conf b/mods/unified_inventory_plus/mod.conf deleted file mode 100644 index 843b02fe..00000000 --- a/mods/unified_inventory_plus/mod.conf +++ /dev/null @@ -1,4 +0,0 @@ -name = unified_inventory_plus -depends = unified_inventory, fakelib -optional_depends = stamina -min_minetest_version = 5.4.0 diff --git a/mods/unified_inventory_plus/settings.lua b/mods/unified_inventory_plus/settings.lua deleted file mode 100644 index 1350c51e..00000000 --- a/mods/unified_inventory_plus/settings.lua +++ /dev/null @@ -1,11 +0,0 @@ -local uip = unified_inventory_plus -local settings = minetest.settings - -uip.settings = { - enable_craft_all = settings:get_bool("unified_inventory_plus.enable_craft_all", true), - enable_craft_organize = settings:get_bool("unified_inventory_plus.enable_craft_organize", true), - enable_craft_rotate = settings:get_bool("unified_inventory_plus.enable_craft_rotate", true), - enable_craft_clear = settings:get_bool("unified_inventory_plus.enable_craft_clear", true), - - craft_all_min_saturation = tonumber(settings:get("unified_inventory_plus.craft_all_min_saturation")) or 1, -} diff --git a/mods/unified_inventory_plus/settingtypes.txt b/mods/unified_inventory_plus/settingtypes.txt deleted file mode 100644 index 29eae3f3..00000000 --- a/mods/unified_inventory_plus/settingtypes.txt +++ /dev/null @@ -1,8 +0,0 @@ -unified_inventory_plus.enable_craft_all (enable "all" button) bool true -unified_inventory_plus.enable_craft_organize (enable template buttons) bool true -unified_inventory_plus.enable_craft_rotate (enable rotate buttons) bool true -unified_inventory_plus.enable_craft_clear (enable "clear" button) bool true - -# Disable the "craft all" feature if saturation (from the stamina mod) is below a given value. -# Set to -1 always allow "craft all". -unified_inventory_plus.craft_all_min_saturation (Minimum saturation for "craft all") int 1 -1 20 diff --git a/mods/unified_inventory_plus/textures/pattern_1.png b/mods/unified_inventory_plus/textures/pattern_1.png deleted file mode 100644 index 437d940e..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_1.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_3.png b/mods/unified_inventory_plus/textures/pattern_3.png deleted file mode 100644 index efa5ebd3..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_3.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_3b.png b/mods/unified_inventory_plus/textures/pattern_3b.png deleted file mode 100644 index 4eda188a..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_3b.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_4.png b/mods/unified_inventory_plus/textures/pattern_4.png deleted file mode 100644 index 3b218d02..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_4.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_5.png b/mods/unified_inventory_plus/textures/pattern_5.png deleted file mode 100644 index 3d706496..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_5.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_6.png b/mods/unified_inventory_plus/textures/pattern_6.png deleted file mode 100644 index 63d612a9..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_6.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_6b.png b/mods/unified_inventory_plus/textures/pattern_6b.png deleted file mode 100644 index 1d2410de..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_6b.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_6c.png b/mods/unified_inventory_plus/textures/pattern_6c.png deleted file mode 100644 index a3765dff..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_6c.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_7.png b/mods/unified_inventory_plus/textures/pattern_7.png deleted file mode 100644 index d403b041..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_7.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_8.png b/mods/unified_inventory_plus/textures/pattern_8.png deleted file mode 100644 index bd5f983b..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_8.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_9.png b/mods/unified_inventory_plus/textures/pattern_9.png deleted file mode 100644 index ecf11a76..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_9.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_clear.png b/mods/unified_inventory_plus/textures/pattern_clear.png deleted file mode 100644 index a54c47a7..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_clear.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_rotate.png b/mods/unified_inventory_plus/textures/pattern_rotate.png deleted file mode 100644 index e398378a..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_rotate.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/pattern_rotate2.png b/mods/unified_inventory_plus/textures/pattern_rotate2.png deleted file mode 100644 index 83419309..00000000 Binary files a/mods/unified_inventory_plus/textures/pattern_rotate2.png and /dev/null differ diff --git a/mods/unified_inventory_plus/textures/ui_crafting_long_arrow.png b/mods/unified_inventory_plus/textures/ui_crafting_long_arrow.png deleted file mode 100644 index 29f7e26a..00000000 Binary files a/mods/unified_inventory_plus/textures/ui_crafting_long_arrow.png and /dev/null differ diff --git a/mods/visible_wielditem b/mods/visible_wielditem new file mode 160000 index 00000000..7794784f --- /dev/null +++ b/mods/visible_wielditem @@ -0,0 +1 @@ +Subproject commit 7794784f52eb8e2fc7a7b342b14d3a7bc701e152 diff --git a/mods/visible_wielditem/Readme.md b/mods/visible_wielditem/Readme.md deleted file mode 100644 index 7c0f2cbd..00000000 --- a/mods/visible_wielditem/Readme.md +++ /dev/null @@ -1,55 +0,0 @@ -# Visible Wielditem - -Shows wielded items in-world. - -## Features - -Modern alternative to [`wield3d`](https://github.com/stujones11/wield3d): - -* Relies less on deprecated engine APIs, doesn't aim to support older MT versions -* Supports colored items. Works well with [`epidermis`](https://github.com/appgurueu/epidermis). -* Supports glow (for environmental lighting use a wielded light mod) -* Indicates size of stacks -* Provides a proper API for mods to use -* Rotates the model instead of the texture - -## License - -Code written by [appgurueu](https://github.com/appgurueu) and licensed under the MIT license. - -The screenshot (`screenshot.png`) uses [Hugues Ross'](https://content.minetest.net/users/Hugues%20Ross/) [RPG16](https://content.minetest.net/packages/Hugues%20Ross/rpg16/) texture pack, which is licensed under CC-BY-SA-4.0, and is therefore licensed under CC-BY-SA-4.0 as well. - -## Links - -* [GitHub](https://github.com/appgurueu/visible_wielditem) - sources, issue tracking, contributing -* [Discord](https://discord.gg/ysP74by) - discussion, chatting -* [Minetest Forum](https://forum.minetest.net/viewtopic.php?f=9&t=27714) - (more organized) discussion -* [ContentDB](https://content.minetest.net/packages/LMD/visible_wielditem/) - releases (downloading from GitHub is recommended) - -## API - -All within the `visible_wielditem` global variable. - -### `get_attachment(modelname, itemname)` - -Returns a table with fields `bonename`, `position` (unit: metric/nodes), `rotation` (unit: degrees) and `scale` (number, unit: metric/nodes) based on model attachments and item tweaks. - -### `model_attachments` - -Table. Keys are model media (file) names, values are tables with field `bonename`, `position`, `rotation` and `scale`. The special field `default` is used for default attachment settings based on `character.b3d` if no model attachments are specified for a player model or if the specified attachment settings are incomplete. - -### `item_tweaks` - -Table of tweaks applied based on the item. Subtable entries have strings as keys and tweak tables with fields `position`, `rotation` and `scale` as values. `position`s are added up, `rotation`s are properly composed, `scale` is multiplied. - -#### `types` - -Applies tweaks based on item type. Possible keys are `unknown`, `node`, `tool` and `craftitem`. - -#### `groups` - -Tweaks for a key are applied if the item has an item group with that name. - -#### `names` - -Tweaks for a single item, by full item name. diff --git a/mods/visible_wielditem/init.lua b/mods/visible_wielditem/init.lua deleted file mode 100644 index e5ce3314..00000000 --- a/mods/visible_wielditem/init.lua +++ /dev/null @@ -1,171 +0,0 @@ -visible_wielditem = {} - -local entity_name = "visible_wielditem:visible_wielditem" - -local entities = {} - -local function create_entity(player) - local pos = player:get_pos() - if not pos then return end -- HACK deal with player object being invalidated before on_leaveplayer has been called - local ent = minetest.add_entity(pos, entity_name) - if ent then - ent:get_luaentity():_set_player(player) - end -end - -minetest.register_on_joinplayer(create_entity) - -minetest.register_on_leaveplayer(function(player) - entities[player:get_player_name()]:_remove() -end) - -visible_wielditem.model_attachments = { - default = { - bonename = "Arm_Right", - position = vector.new(0, 0.375, 0), - rotation = vector.new(180, 90, 0), - scale = 0.25 - } -} - -visible_wielditem.item_tweaks = { - types = { - unknown = {}, - node = { - position = vector.new(0, 0.2, 0), - rotation = vector.new(0, 0, 180) - }, - tool = { - position = vector.new(0, 0, 0.3), - rotation = vector.new(0, 0, 135) - }, - craft = { - position = vector.new(0, 0, 0.3), - rotation = vector.new(0, 0, 45), - } - }, - groups = {}, - names = {} -} - -local quaternion_from_euler_rot_deg = modlib.quaternion.from_euler_rotation_deg - -local quaternion_to_euler_rot_deg = modlib.quaternion.to_euler_rotation_irrlicht - --- HACK quaternion multiplication should be fixed in modlib eventually -local function quaternion_compose(other, self) - local X, Y, Z, W = unpack(self) - return modlib.quaternion.normalize { - (other[4] * X) + (other[1] * W) + (other[2] * Z) - (other[3] * Y), - (other[4] * Y) + (other[2] * W) + (other[3] * X) - (other[1] * Z), - (other[4] * Z) + (other[3] * W) + (other[1] * Y) - (other[2] * X), - (other[4] * W) - (other[1] * X) - (other[2] * Y) - (other[3] * Z), - } -end - -function visible_wielditem.get_attachment(modelname, itemname) - local model_attachments = visible_wielditem.model_attachments - local attachment = modlib.table.copy(model_attachments[modelname] or model_attachments.default) - local rotation = quaternion_from_euler_rot_deg(attachment.rotation) - local function apply_tweaks(tweaks) - tweaks = tweaks or {} - if tweaks.position then attachment.position = vector.add(attachment.position, tweaks.position) end - if tweaks.rotation then - rotation = quaternion_compose(rotation, quaternion_from_euler_rot_deg(tweaks.rotation)) - end - if tweaks.scale then attachment.scale = attachment.scale * tweaks.scale end - end - local def = minetest.registered_items[itemname] or {} - local item_tweaks = visible_wielditem.item_tweaks - apply_tweaks(item_tweaks.types[def.type or "unknown"]) - for groupname, rating in pairs(def.groups or {}) do - if rating ~= 0 then - apply_tweaks(item_tweaks.groups[groupname]) - end - end - apply_tweaks(item_tweaks.names[itemname]) - attachment.rotation = quaternion_to_euler_rot_deg(rotation) - return attachment -end - -minetest.register_entity(entity_name, { - initial_properties = { - physical = false, - collide_with_objects = false, - pointable = false, - visual = "wielditem", - wield_item = "", - is_visible = false, - backface_culling = false, - use_texture_alpha = true, - glow = 0, - static_save = false, - shaded = true - }, - on_activate = function(self) - local object = self.object - object:set_armor_groups { immortal = 1 } - end, - _force_resend = function(self) -- HACK - self.object:set_pos(self.object:get_pos()) - end, - _update_attachment = function(self) - if not self._item then return end - local object = self.object - local attachment = visible_wielditem.get_attachment(self._player:get_properties().mesh, - self._item:get_name() or "") - -- TODO softcode - local stack_scale = 1 - if self._item:get_definition().type ~= "tool" then - stack_scale = 0.75 + 0.5 * math.min(1, self._item:get_count() / self._item:get_stack_max()) - end - local vsize = attachment.scale * stack_scale - object:set_properties { visual_size = vector.new(vsize, vsize, vsize) } - object:set_attach(self._player, - attachment.bonename, - vector.multiply(vector.offset(attachment.position, 0, vsize / 2, 0), 10), - attachment.rotation) - self:_force_resend() - end, - _set_player = function(self, player) - self._player = player -- HACK this assumes that PlayerRefs don't change - self:_set_item(player:get_wielded_item()) - self:_update_attachment() - entities[player:get_player_name()] = self - end, - on_deactivate = function(self) - create_entity(self._player) - end, - _set_item = function(self, item) - self._item = item - local object = self.object - if item:is_empty() then - object:set_properties { - is_visible = false, - wield_item = "" - } - return - end - object:set_properties { - is_visible = true, - wield_item = item:to_string(), - glow = item:get_definition().light_source or 0 - } - self:_update_attachment() - end, - _remove = function(self) - self.on_deactivate = modlib.func.no_op -- don't recreate entity; it's supposed to be removed - self.object:remove() - entities[self._player:get_player_name()] = nil - end - -- TODO on_step: reattach regularly to work around engine bugs? -}) - -modlib.minetest.register_on_wielditem_change(function(player, _, _, item) - local entity = entities[player:get_player_name()] - if entity.object:get_pos() then - entity:_set_item(item) - else -- recreate entity if necessary - create_entity(player) - end -end) diff --git a/mods/visible_wielditem/mod.conf b/mods/visible_wielditem/mod.conf deleted file mode 100644 index 2214c494..00000000 --- a/mods/visible_wielditem/mod.conf +++ /dev/null @@ -1,4 +0,0 @@ -name = visible_wielditem -description = Shows your wielditem -depends = modlib -min_minetest_version = 5.1 \ No newline at end of file diff --git a/types/core.d.lua b/types/core.d.lua deleted file mode 100644 index 80e8ec2f..00000000 --- a/types/core.d.lua +++ /dev/null @@ -1,835 +0,0 @@ ----@meta ----@version 5.1|JIT - --- This work is marked with CC0 1.0 - https://creativecommons.org/publicdomain/zero/1.0/ --- by frog :D --- Types for luanti - ----@class vector ----@field x number ----@field y number ----@field z number -vector = {} - ----@alias vectorT table|vector - ----@param x number ----@param y number ----@param z number ----@return vector -function vector.new(x, y, z) end - ----@return vector -function vector.zero() end - ----@param v vectorT ----@return vector -function vector.copy(v) end - ----Returns v, np, where v is a vector read from the given string s and np is the next position in the string after the vector. ----Returns nil on failure. ---- s: Has to begin with a substring of the form "(x, y, z)". Additional spaces, leaving away commas and adding an additional comma to the end is allowed. ----@return vector ----@param init integer? ----@param s string -function vector.from_string(s, init) end - ----@param v vectorT ----@return string -function vector.to_string(v) end - ----@param p1 vectorT ----@param p2 vectorT ----@return vector -function vector.direction(p1, p2) end - ----@param p1 vectorT ----@param p2 vectorT ----@return number -function vector.distance(p1, p2) end - ----@param v vectorT ----@return number -function vector.length(v) end - ----@param v vectorT ----@return vectorT -function vector.normalize(v) end - ----@param v vectorT ----@return vectorT -function vector.floor(v) end - ----@param v vectorT ----@return vectorT -function vector.round(v) end - ----@param v vectorT ----@param func function ----@return vector -function vector.round(v, func) end - ----@param v vectorT ----@param w vectorT ----@param func function ----@return vector -function vector.combine(v, w, func) end - ----@param v1 vectorT ----@param v2 vectorT ----@return boolean -function vector.equals(v1, v2) end - ----@param v1 vectorT ----@param v2 vectorT ----@return vector -function vector.sort(v1, v2) end - ----@param v1 vectorT ----@param v2 vectorT ----@return vector -function vector.angle(v1, v2) end - ----@param v1 vectorT ----@param v2 vectorT ----@return vector -function vector.dot(v1, v2) end - ----@param v1 vectorT ----@param v2 vectorT ----@return vector -function vector.cross(v1, v2) end - ----@param v1 vectorT ----@param v2 vectorT ----@return vector -function vector.offset(v1, v2) end - ----@param v any ----@return boolean -function vector.check(v) end - ----@param v number|vectorT ----@param x number|vectorT ----@return vector -function vector.add(v, x) end - ----@param v vectorT ----@param x number|vectorT ----@return vector -function vector.subtract(v, x) end - ----@param v vectorT ----@param x number ----@return vector -function vector.multiply(v, x) end - ----@param v vectorT ----@param x number|vectorT ----@return vector -function vector.divide(v, x) end - ----@param x vectorT ----@param min vectorT ----@param max vectorT ----@return boolean -function vector.in_area(x, min, max) end - ----@param pos vectorT ----@return number -function core.hash_node_position(pos) end - ----@class node ----@field name string ----@field param2 integer ----@field param1 integer - ----@class ObjectRef - -core = {} - ----@deprecated -minetest = core - ----@alias alias "mapgen_stone" | "mapgen_water_source" | "mapgen_river_water_source" | "mapgen_lava_source" | "mapgen_cobble" | "mapgen_singlenode" | string - ----@param alias alias ----@param original_name string ----@return nil -function core.register_alias(alias, original_name) end - ---- The only difference between core.register_alias and core.register_alias_force is that if an item named alias already exists, core.register_alias will do nothing while core.register_alias_force will unregister it. ----@param alias alias ----@param original_name string ----@return nil -function core.register_alias_force(alias, original_name) end - ----@class vector - ----@class sound_def ----@field gain number ----@field pitch number ----@field fade number ----@field start_time number ----@field loop boolean ----@field pos vector ----@field object ObjectRef ----@field to_player string ----@field exclude_player string ----@field max_hear_distance number - ----@class node_def: any - ----@type table -core.registered_nodes = {} - ---- x1, y1, z1, x2, y2, z2 ----@alias box table | nil ----@alias box_or_boxes box | table | nil - ----@class nodebox ----@field type string ----@field fixed box_or_boxes ----@field wall_top box ----@field wall_bottom box ----@field wall_side box ----@field connect_top box_or_boxes ----@field connect_bottom box_or_boxes ----@field connect_front box_or_boxes ----@field connect_left box_or_boxes ----@field connect_back box_or_boxes ----@field connect_right box_or_boxes ----@field disconnected_top box_or_boxes ----@field disconnected_bottom box_or_boxes ----@field disconnected_front box_or_boxes ----@field disconnected_left box_or_boxes ----@field disconnected_back box_or_boxes ----@field disconnected_right box_or_boxes ---- When there is *no* neighbour ----@field disconnected box_or_boxes ---- when there are *no* neighbours to the sides ----@field disconnected_sides box_or_boxes - ----@alias pointed_thing { type: "nothing" } | { type: "node", under: vector, above: vector, intersection_point: vector | nil, box_id: integer, intersection_normal: vector | nil} | { type: "object", ref: ObjectRef, intersection_point: vector | nil, box_id: integer, intersection_normal: vector | nil } - ----@class ItemStack_table ----@field name string ----@field count integer | nil ----@field wear integer | nil ----@field metadata string | nil - ----@alias group_type integer | nil - ----@class groups: { [string]: group_type } ----@field attached_node group_type ----@field bouncy group_type ----@field connect_to_raillike group_type ----@field dig_immediate group_type ----@field disable_jump group_type ----@field disable_descent group_type ----@field fall_damage_add_percent group_type ----@field falling_node group_type ----@field float group_type ----@field level group_type ----@field slippery group_type ----@field disable_repair group_type - ----@class armor_groups: { [string]: group_type } ----@field immortal group_type ----@field fall_damage_add_percent group_type ----@field punch_operable group_type - ----@param itemname string ----@param groupname string ----@return integer -function core.get_item_group(itemname, groupname) end - ----@class ItemStack:userdata - ---- #RGB defines a color in hexadecimal format. ----#RGBA defines a color in hexadecimal format and alpha channel. ----#RRGGBB defines a color in hexadecimal format. ----#RRGGBBAA defines a color in hexadecimal format and alpha channel. ----Named colors are also supported and are equivalent to CSS Color Module Level 4. To specify the value of the alpha channel, append #A or #AA to the end of the color name (e.g. colorname#08). ----@class ColorString:string - ----A ColorSpec specifies a 32-bit color. It can be written in any of the following forms: ---- table form: Each element ranging from 0..255 (a, if absent, defaults to 255): ---- colorspec = {a=255, r=0, g=255, b=0} ---- numerical form: The raw integer value of an ARGB8 quad: ---- colorspec = 0xFF00FF00 ---- string form: A ColorString (defined above): ---- colorspec = "green" ----@alias ColorSpec string|table|integer - ----@param color ColorString ----@return string -function core.get_color_escape_sequence(color) end - ----@param color ColorString ----@param message string ----@return string -function core.colorize(color, message) end - ----@param color ColorString ----@return string -function core.get_background_escape_sequence(color) end - ----@param str string ----@return string -function core.strip_foreground_colors(str) end - ----@param str string ----@return string -function core.strip_background_colors(str) end - ----@param str string ----@return string -function core.strip_colors(str) end - ----@param obj any ----@param name string? ----@param dumped table? ----@return string -function dump2(obj, name, dumped) end - ----@param obj any ----@param dumped table? ----@return string -function dump(obj, dumped) end - ----@param x number ----@param y number ----@return number -function math.hypot(x, y) end - ----@param x number ----@param tolerance number | nil ----@return number -function math.sign(x, tolerance) end - ----@param x number ----@return number -function math.factorial(x) end - ----@param x number ----@return number -function math.round(x) end - ----@param str string ----@param separator string | nil ----@param include_empty boolean|nil ----@param max_splits integer|nil ----@param sep_is_pattern boolean|nil ----@return table -function string.split(str, separator, include_empty, max_splits, sep_is_pattern) end - ----@return string -function string:trim() end - ----@param str string ----@param limit integer | nil ----@param as_table boolean | nil ----@return string | table -function core.wrap_text(str, limit, as_table) end - ----@param pos vector ----@param decimal_places integer | nil ----@return string -function core.pos_to_string(pos, decimal_places) end - ----@param relative_to vector | nil ----@param str string ----@return [vector, vector] -function core.string_to_area(str, relative_to) end - ----@param string string ----@return string -function core.formspec_escape(string) end - ----@param arg string ----@return string -function core.is_yes(arg) end - ----@param arg number ----@return boolean -function core.is_nan(arg) end - ----@return number -function core.get_us_time() end - ----@param table table ----@return table -function table.copy(table) end - ----@param list table ----@param val number ----@return integer -function table.indexof(list, val) end - ----@param table table ----@param other_table table -function table.insert_all(table, other_table) end - ----@param t table ----@return table -function table.key_value_swap(t) end - ----@param t table ----@param from integer? ----@param to integer? ----@param random_func function? -function table.shuffle(t, from, to, random_func) end - ----@param pointed_thing pointed_thing ----@param placer ObjectRef ----@return vector -function core.pointed_thing_to_face_pos(placer, pointed_thing) end - ----@param uses integer ----@param initial_wear integer? ----@return integer -function core.get_tool_wear_after_use(uses, initial_wear) end - ---- TEMPORARY CLASSES YO HEY ----@class tool_capabilities: table ----@class dig_params: table ----@class hit_params: table ----@class item_def: table ----@class abm_def: table ----@class lbm_def: table ----@class biome_def: table ----@class luaentity_def: table ----@class ore_def: table ----@class deco_def: table ----@class recipe_def: table ----@class priv_def: table ----@class chatcommand_def: table ----@class schem_def: table - ----@param tool_capabilities tool_capabilities ----@param groups table no its not the groups type for a reason ----@param wear integer? ----@return dig_params -function core.get_dig_params(groups, tool_capabilities, wear) end - ----@param groups table ----@param tool_capabilities tool_capabilities ----@param time_from_last_punch integer? ----@param wear integer? ----@return hit_params -function core.get_hit_params(groups, tool_capabilities, time_from_last_punch, wear) end - ----@param textdomain string? ----@return fun(str: string, ...:string ): string -function core.get_translator(textdomain) end - ----@param textdomain string? ----@param str string ----@vararg string -function core.translate(textdomain, str, ...) end - ----@param lang_code string ----@param string string -function core.get_translated_string(lang_code, string) end - ----@class NoiseParams ----@field offset integer ----@field scale integer ----@field spread vector ----@field seed integer ----@field octaves integer ----@field persistence integer ----@field lacunarity integer ----@field flags table|string|nil - ----@param p1 vector? ----@param p2 vector? ----@return VoxelManip -function core.get_voxel_manip(p1, p2) end - ----@class VoxelManip ----@operator call:fun(p1, p2):VoxelManip -VoxelManip = {} - ----returns actual emerged pmin, actual emerged pmax ----@param p1 vector ----@param p2 vector ----@return [vector, vector] -function VoxelManip:read_from_map(p1, p2) end - ----@param light boolean? -function VoxelManip:write_to_map(light) end - ----@param pos vector ----@return node -function VoxelManip:get_node_at(pos) end - ----@param pos vector ----@param node node -function VoxelManip:set_node_at(pos, node) end - ----@class content_ids:integer - ----@param buffer table? ----@return table -function VoxelManip:get_data(buffer) end - ----@param data table -function VoxelManip:set_data(data) end - ----Does literally nothing lmao ----@deprecated ----@return nil -function VoxelManip:update_map() end - ---- day = 0...15, night = 0...15 ----@alias vmanip_light { day: integer, night: integer } - ----@param light vmanip_light ----@param p1 vector? ----@param p2 vector? -function VoxelManip:set_lighting(light, p1, p2) end - ----@param buffer table? ----@return table? -function VoxelManip:get_light_data(buffer) end - ----@param param2_data table ----@return nil -function VoxelManip:set_param2_data(param2_data) end - ----@param p1 vector? ----@param p2 vector? ----@param propagate_shadow boolean? -function VoxelManip:calc_lighting(p1, p2, propagate_shadow) end - ----@return nil -function VoxelManip:update_liquids() end - ----@return boolean -function VoxelManip:was_modified() end - ----@return [vector, vector] -function VoxelManip:get_emerged_area() end - ----@class VoxelArea -VoxelArea = {} ----@operator call:fun(pmin, pmax):VoxelArea|fun(edges: { MinEdge: vector, MaxEdge: vector }):VoxelArea - ----@param pmin vector ----@param pmax vector ----@return VoxelArea -function VoxelArea:new(pmin, pmax) end - ----@param t { MinEdge: vector, MaxEdge: vector } ----@return VoxelArea -function VoxelArea:new(t) end - ----@return vector -function VoxelArea:getExtent() end - ----@return vector -function VoxelArea:getVolume() end - ----@param x number ----@param y number ----@param z number ----@return integer -function VoxelArea:index(x, y, z) end - ----@param p number ----@return integer -function VoxelArea:indexp(p) end - ----@param i integer ----@return vector -function VoxelArea:position(i) end - ----@param x number ----@param y number ----@param z number ----@return boolean -function VoxelArea:contains(x, y, z) end - ----@param p vector ----@return boolean -function VoxelArea:containsp(p) end - ----@param i integer ----@return boolean -function VoxelArea:containsi(i) end - ----@param minx number ----@param miny number ----@param minz number ----@param maxx number ----@param maxy number ----@param maxz number ----@nodiscard -function VoxelArea:iter(minx, miny, minz, maxx, maxy, maxz) end - ----@param minp vector ----@param maxp vector ----@nodiscard -function VoxelArea:interp(minp, maxp) end - ----@class l_system_tree_def ----@field axiom string ----@field rules_a string|nil ----@field rules_b string|nil ----@field rules_c string|nil ----@field rules_d string|nil ----@field trunk string ----@field leaves string ----@field leaves2 string ----@field leaves2_chance string ----@field angle number ----@field iterations number ----@field random_level number ----@field trunk_type "single"|"double"|"crossed" ----@field thin_branches boolean ----@field fruit string ----@field fruit_chance number ----@field seed number|nil - ----@return string -function core.get_current_modname() end - ----@param modname string ----@return string -function core.get_modpath(modname) end - ----@return string[] -function core.get_modnames() end - ----@return { id: string, title:string, author: string, path:string} -function core.get_game_info() end - ----@return string -function core.get_worldpath() end - ----@return string -function core.get_mod_data_path() end - ----@return boolean -function core.is_singleplayer() end - ---- no im not gonna type the entire thing out, NO, no chance ----@type table -core.features = {} - ----@param arg string | table ----@return boolean, table -function core.has_feature(arg) end - ----@class player_info_def ----@field address string, # IP address of client ----@field ip_version number, # IPv4 / IPv6 ----@field connection_uptime number, # seconds since client connected ----@field protocol_version number, # protocol version used by client ----@field formspec_version number, # supported formspec version ----@field lang_code string, # Language code used for translation ----@field min_rtt number? ----@field max_rtt number? ----@field avg_rtt number? ----@field min_jitter number? ----@field max_jitter number? ----@field avg_jitter number? - ----@alias vector2 { x: number, y:number } - ----@class window_info_def ----@field size vector2 ----@field max_formspec_size vector2 ----@field real_gui_scaling number ----@field real_hud_scaling number ----@field touch_controls boolean - ----@param player_name string ----@return player_info_def -function core.get_player_information(player_name) end - ----@param player_name string ----@return window_info_def -function core.get_player_window_information(player_name) end - ----@param path string ----@return boolean -function core.mkdir(path) end - ----@param path string ----@param recursive boolean ----@return boolean -function core.rmdir(path, recursive) end - ----@param source string ----@param destination string ----@return boolean -function core.cpdir(source, destination) end - ----@param source string ----@param destination string ----@return boolean -function core.mvdir(source, destination) end - ----@param is_dir boolean? ----@param path string ----@return string[] -function core.get_dir_list(path, is_dir) end - ----Replaces contents of file at path with new contents in a safe (atomic) way. Use this instead of below code when writing e.g. database files: local f = io.open(path, "wb"); f:write(content); f:close() ----@param path string ----@param content string ----@return boolean -function core.safe_file_write(path, content) end - ----@class mt_version_def ----@field project string ----@field string string ----@field proto_min integer ----@field proto_max integer ----@field hash string ----@field is_dev boolean - ----@return mt_version_def -function core.get_version() end - ----@param data string ----@param raw? boolean ----@return string -function core.sha1(data, raw) end - ----@return string ----@param data string ----@param raw boolean? -function core.sha256(data, raw) end - ----@param colorspec ColorSpec|any ----@return ColorString? -function core.colorspec_to_colorstring(colorspec) end - ----@param colorspec ColorSpec|any ----@return string? -function core.colorspec_to_bytes(colorspec) end - ----@return string ----@param width integer ----@param height integer ----@param data ColorSpec[]|string ----@param compression integer? -function core.encode_png(width, height, data, compression) end - ----@param str string ----@return string -function core.urlencode(str) end - ----@param ... any -function core.debug(...) end - ----@param level "none"|"error"|"warning"|"action"|"info"|"verbose" ----@param text string ----@return nil -function core.log(level, text) end - ----@param text string ----@return nil -function core.log(text) end - ----@param name string ----@param node_def node_def ----@return nil -function core.register_node(name, node_def) end - ----@param name string ----@param item_def item_def ----@return nil -function core.register_craftitem(name, item_def) end - ----@param name string ----@param item_def item_def ----@return nil -function core.register_tool(name, item_def) end - ----@param name string ----@param redefinition table ----@param del_fields table? ----@return nil -function core.override_item(name, redefinition, del_fields) end - ----@param name string ----@return nil -function core.unregister_item(name) end - ----@param name string ----@param luaentity_def luaentity_def ----@return nil -function core.register_entity(name, luaentity_def) end - ----@param abm_def abm_def ----@return nil -function core.register_abm(abm_def) end - ----@param lbm_def lbm_def ----@return nil -function core.register_lbm(lbm_def) end - ----@param ore_def ore_def ----@return nil -function core.register_ore(ore_def) end - ----@param biome_def biome_def ----@return nil -function core.register_biome(biome_def) end - ----@param name string ----@return nil -function core.unregister_biome(name) end - ----@param deco_def deco_def ----@return nil -function core.register_decoration(deco_def) end - ----@param schem_def schem_def ----@return nil -function core.register_schematic(schem_def) end - ----@return nil -function core.clear_registered_biomes() end - ----@return nil -function core.clear_registered_decorations() end - ----@return nil -function core.clear_registered_schematics() end - ----@param recipe recipe_def ----@return nil -function core.register_craft(recipe) end - ----@param recipe recipe_def ----@return nil -function core.clear_craft(recipe) end - ----@param chatcommand_def chatcommand_def ----@return nil -function core.register_chatcommand(cmd, chatcommand_def) end - ----@param redef table ----@param name string ----@return nil -function core.override_chatcommand(name, redef) end - ----@param name string ----@param redef table ----@return nil -function core.unregister_chatcommand(name, redef) end - ----@param def priv_def ----@param name string ----@return nil -function core.register_privilege(name, def) end - ---- this function is so absurdly uncommon im not going to bother ----@param auth_handler_def table -function core.register_authentication_handler(auth_handler_def) end