code:
# Translation mostly by Xanthin

### api.lua ###
Digging (by chance) = Graben (durch Zufall)

### bags.lua ###
Bags = Taschen
Bag @1 = Tasche @1
Small Bag = Kleine Tasche
Medium Bag = Mittelgroße Tasche
Large Bag = Große Tasche

### inernal.lua ###
First page = Erste Seite
Back three pages = Drei Seiten zurueckblättern
Back one page = Eine Seite zurueckblättern
Forward one page = Eine Seite vorblättern
Forward three pages = Drei Seiten vorblättern
Last page = Letzte Seite
No matching items = Keine passenden Gegenstände
Page = Seite
%s of %s = %s von %s
Filter = Filter
Search = Suchen
Reset search and display everything = Suche zurücksetzen und alles anzeigen

### register.lua ###
Can use the creative inventory = Kann das Kreativinventar nutzen
Home position set to: %s = Heimatposition nach: %s gesetzt
Time of day set to 6am = Tageszeit auf 6 Uhr geändert
You don't have the settime privilege! = Du hast nicht das „settime“-Privileg!
Time of day set to 9pm = Tageszeit auf 21 Uhr geändert
This button has been disabled outside of creative mode to prevent accidental inventory trashing. Use the trash slot instead. = Diese Funktion ist außerhalb des Kreativmodus deaktiviert, um ein versehentliches Löschen des ganzen Inventars zu verhindern.\nNutze stattdessen das Müllfeld.
Inventory cleared! = Inventar geleert!
Crafting = Fertigung
Digging = Graben
Cooking = Kochen
Mixing = Mischen
Trash: = Müll:
Refill: = Nachfüllen:
Crafting Guide = Fertigungsführer
Method: = Methode:
Result: %s = Ergebnis: %s
crafting = Bauen
shapeless crafting = Formlose Fertigung
cooking = Kochen
alloy cooking = Legierung Kochen
Copy to craft grid: = Ins Fertigungsraster kopieren:
All = Alles
Alternate = Alternative
Crafting Grid = Fertigungsraster
Show next recipe = Nächstes Rezept zeigen
Show next usage = Nächste Verwendung zeigen
Show previous recipe = Vorheriges Rezept zeigen
Show previous usage = Vorherige Verwendung zeigen
This recipe is too\nlarge to be displayed. = Dieses Rezept ist zu\ngroß, um angezeigt\nzu werden.
Any item belonging to the %s group = Irgendein Gegenstand, der zur Gruppe %s gehört
Any item belonging to the groups %s = Irgendein Gegenstand, der zu den Gruppen %s gehört
Recipe %d of %d = Rezept %d von %d
Usage %d of %d = Verwendung %d von %d
No recipes = Keine Rezepte
No usages = Keine Verwendungen
Result = Ergebnis
Ingredient = Zutat
Set time to day = Zur Tageszeit wechseln
Set time to night = Zur Nachtzeit wechseln
Set home position = Heimatposition setzen
Go home = Nach Hause gehen
Clear inventory = Inventar leeren
Give me: = Gib mir:
To craft grid: = Ins Fertigungsraster:

### group.lua ###
\sand\s=\sund\s

### waypoints.lua ###
White = Weiß
Yellow = Gelb
Red = Rot
Green = Grün
Blue = Blau
Waypoints = Wegpunkte
Waypoint %d = Wegpunkt Nr. %d
Waypoint active = Wegpunkt aktiv
Waypoint inactive = Wegpunkt inaktiv
World position = Weltposition
Name = Name
HUD text color = HUD-Textfarbe
Edit waypoint name = Name des Wegpunkts ändern
Rename waypoint = Wegpunkt umbenennen
Change color of waypoint display = Farbe der Darstellung der Wegpunkte ändern
Set waypoint to current location = Setze Wegpunkt zur derzeitigen Position
Make waypoint visible = Wegpunkt sichtbar machen
Make waypoint invisible = Wegpunkt verstecken
Disable display of waypoint coordinates = Anzeige der Wegpunktkoordinaten deaktivieren
Enable display of waypoint coordinates = Anzeige der Wegpunktkoordinaten aktivieren
Finish editing = Bearbeitung abschließen
Select Waypoint #%d = Wegpunkt Nr. %d auswählen


code:
# Translation by Diego Martínez <kaeza>

### api.lua ###
Digging (by chance) = Excavado (por azar)

# Template
### bags.lua ###
Bags = Bolsas
Bag @1 = Bolsa @1
Small Bag = Bolsa Pequeña
Medium Bag = Bolsa Mediana
Large Bag = Bolsa Grande

### inernal.lua ###
First page =
Back three pages =
Back one page =
Forward one page =
Forward three pages =
Last page =
No matching items =
Page = Página
%s of %s = %s de %s
Filter = Filtro
Search =

### register.lua ###
Can use the creative inventory = Puede usar el inventario creativo
Home position set to: %s = Posición de hogar cambiada a: %s
Time of day set to 6am = Hora del día cambiada a 6AM
You don't have the settime priviledge! = ¡No tienes el privilegio `settime'!
Time of day set to 9pm = Hora del día cambiada a 9PM
This button has been disabled outside of creative mode to prevent accidental inventory trashing. Use the trash slot instead. = Éste botón ha sido deshabilitado para prevenir la destrucción accidental del inventario.\nUsa la ranura para basura en su lugar.
Inventory cleared! = ¡Inventario limpio!
Crafting = Elaboración
Trash: = Basura:
Refill: = Rellenar:
Crafting Guide = Guía de Elaboración
Method: = Método:
Result: %s = Resultado: %s
crafting = elaboración
shapeless crafting = elaboración sin forma
cooking = hornear
alloy cooking = horneado de aleación
Copy to craft grid: = Copiar al cuadro de elaboración
All = Todos
Recipe %s of %s = Receta %s de %s
Alternate = Alternar
Crafting Grid =

### waypoints.lua ###
White = Blanco
Yellow = Amarillo
Red = Rojo
Green = Verde
Blue = Azul
Waypoints = Puntos de paso
Waypoint active = Punto de paso activo
Waypoint inactive = Punto de paso inactivo
World position = Posición en el mundo
Name = Nombre
HUD text color = Color del HUD
Edit waypoint name =
Rename waypoint =
Change color of waypoint display =
Set waypoint to current location =
Make waypoint visible =
Make waypoint invisible =
Disable display of waypoint coordinates =
Enable display of waypoint coordinates =
Finish editing =
Select Waypoint #%d =

repo_name: Desour/PlayMinetestServer

license: mit


code:
# Translation by kilbith

# Template
### bags.lua ###
Bags = Sacs
Bag @1 = Sac @1
Small Bag = Petit sac
Medium Bag = Sac moyen
Large Bag = Grand sac

### inernal.lua ###
First page = 1ère page
Back three pages = 3 pages en arrière
Back one page = Page précédente
Forward one page = Page suivante
Forward three pages = 3 pages en avant
Last page = Dernière page
No matching items = Aucun élément correspondant
Page = Page
%s of %s = %s de %s
Filter = Filtre
Search = Rechercher

### register.lua ###
Can use the creative inventory = Vous pouvez utiliser l'inventaire créatif
Home position set to: %s = Position de votre base fixée à : %s
Time of day set to 6am = Heure fixée à 6h
You don't have the settime priviledge! = Vous n'avez pas le privilège 'settime' !
Time of day set to 9pm = Heure fixée à 21h
This button has been disabled outside of creative mode to prevent accidental inventory trashing.\nUse the trash slot instead. = Ce bouton a été désactivé en dehors du mode créatif pour éviter des saccages dans l'inventaire.\nUtilisez plutôt la case poubelle.
Inventory cleared! = Inventaire vidé !
Crafting = Création
Trash: = Poubelle :
Refill: = Remplir :
Crafting Guide = Guide de création
Method: = Méthode :
Result: %s = Résultat : %s
crafting = fabrication
shapeless crafting = fabrication sans forme
cooking = cuisson
alloy cooking = cuisson des métaux
Copy to craft grid: = Copier sur la grille de création
All = Tout
Recipe %s of %s = Recette %s de %d
Alternate = Alternative
Crafting Grid =  Grille de création

### waypoints.lua ###
White = Blanc
Yellow = Jaune
Red = Rouge
Green = Vert
Blue = Bleu
Waypoints = Point de passage
Waypoint active = Point de passage actif
Waypoint inactive = Point de passage inactif
World position = Position dans le monde
Name = Nom
HUD text color = Couleur de texte du HUD
Edit waypoint name = Editer le nom du point de passage
Rename waypoint = Renommer le point de passage
Change color of waypoint display = Changer la couleur du point de passage
Set waypoint to current location = Marquer un point de passage à la position actuelle
Make waypoint visible = Rendre visible le point de passage
Make waypoint invisible = Rendre invisible le point de passage
Disable display of waypoint coordinates = Masquer les coordonnées des points de passages
Enable display of waypoint coordinates = Montrer les coordonnées des points de passages
Finish editing = Terminer l'édition
Select Waypoint #%d = Choisir un point de passage #%d


code:
# Translation by RealBadAngel

### bags.lua ###
Bags = Plecaki
Bag @1 = Plecak @1
Small Bag = Maly plecak
Medium Bag = Sredni plecak
Large Bag = Duzy plecak

### inernal.lua ###
First page = Pierwsza strona
Back three pages = 3 strony w tyl
Back one page = 1 strona w tyl
Forward one page = 1 strona do przodu
Forward three pages = 3 strony do przodu
Last page = Ostatnia strona
No matching items = Brak pasujacych przedmiotow
Page = Strona
%s of %s = %s z %s
Filter = Filtr
Search = Szukaj

### register.lua ###
Can use the creative inventory =
Home position set to: %s = Pozycja domowa ustawiona na: %s
Time of day set to 6am = Czas ustawiony na 6:00
You don't have the settime priviledge! = Nie masz uprawnien do zmiany czasu (settime)!
Time of day set to 9pm = Czas ustawiony na 21:00
This button has been disabled outside of creative mode to prevent accidental inventory trashing.\nUse the trash slot instead. =
Inventory cleared! =
Crafting =
Trash: = Smietnik:
Refill: = Uzupelnianie:
Crafting Guide =
Method: = Metoda:
Result: %s = Wynik: %s
crafting =
shapeless crafting =
cooking =
alloy cooking =
Copy to craft grid: =
All = Wszystko
Recipe %s of %s = Recepta %s z %s
Alternate = Alternatywa
Crafting Grid =

### waypoints.lua ###
White = Bialy
Yellow = Zolty
Red = Czerwony
Green = Zielony
Blue = Niebieski
Waypoints = Punkty orientacyjne
Waypoint active = Punkt wlaczony
Waypoint inactive = Punkt wylaczony
World position = Pozycja
Name = Nazwa
HUD text color = Kolor tekstu HUD
Edit waypoint name = Edytuj nazwe punktu
Rename waypoint = Zmien nazwe punktu
Change color of waypoint display = Zmien kolor punktu
Set waypoint to current location = Ustaw punkt orientacyjny na biezacej pozycji
Make waypoint visible = Pokaz punkt
Make waypoint invisible = Nie pokazuj punktu
Disable display of waypoint coordinates = Pokazuj koordynaty punktu
Enable display of waypoint coordinates = Nie pokazuj koordynatow punktu
Finish editing = Zakoncz edycje
Select Waypoint #%d = Wybierz punkt #%d


code:
# Translation by Lunovox <lunovox@openmailbox.org>

### api.lua ###
Digging (by chance) = Cavando (por acaso)

# Template
### bags.lua ###
Bags = Bolsas
Bag @1 = Bolsa @1
Small Bag = Bolsa Pequena
Medium Bag = Bolsa Média
Large Bag = Bolsa Grande

### inernal.lua ###
First page = Primeira Página
Back three pages =  Voltar 3 Páginas
Back one page = Voltar 1 Página
Forward one page = Avançar 1 Página
Forward three pages = Avançar 3 Páginas
Last page = Ultima Página
No matching items = Nenhum item correspondente
Page = Página
%s of %s = %s de %s
Filter = Filtro
Search = Pesquisar
Reset search and display everything = Redefinir pesquisa e exibir tudo

### register.lua ###
Can use the creative inventory = Pode usar o inventário do criativo
Forces Unified Inventory to be displayed in Full mode if Lite mode is configured globally = Força o Unified Inventory a ser exibido no modo Full se o modo Lite estiver configurado globalmente
Home position set to: %s = Posição inicial definida para: %s
Time of day set to 6am = Hora do dia definida para 06h
You don't have the settime priviledge! = Você não tem o privilégio de 'settime'!
Time of day set to 9pm = Hora do dia ajustada para 21h
This button has been disabled outside of creative mode to prevent accidental inventory trashing.\nUse the trash slot instead. = Este botão foi desativado fora do modo de criativo para evitar o descarte acidental de inventário. \nUse o slot de lixo em vez disso.
Inventory cleared! = Inventário Apagado!
Crafting = Artesanato
Digging = Escavação
Cooking = Cozimento
Mixing = Muistura
Trash: = Lixo:
Refill: = Recarga:
Crafting Guide = Guia de Artesanato
Method: = Método:
Result: %s = Resultado: %s
crafting = artesanato
shapeless crafting = artesanato sem formato
cooking = cozinhar
alloy cooking = Cozinhar em liga
Copy to craft grid: = Copiar para grade de artesanato:
All = MAX
Alternate = Alternar
Crafting Grid = Grade de Artesanato
Show next recipe = Exibir Próxima Receita
Show next usage = Mostrar Próxima Utilização
Show previous recipe = Exibir Receita Anterior
Show previous usage = Exibir Utilização Anterior

# Shown for huge crafting recipes; try to keep the line length short and use multiple line breaks as needed
This recipe is too\nlarge to be displayed. = Esta receita é grande \ndemais para ser apresentada.

# %s = group name (e.g. wool)
Any item belonging to the %s group = Qualquer item pertencente ao grupo '%s'.

# %s = List of “and”-concatenated group names
Any item belonging to the groups %s = Qualquer item pertencente aos grupos '%s'.
Recipe %d of %d = Receita %s de %s
Usage %d of %d = Utilização %s de %s
No recipes = Sem Receita
No usages = Sem Utilização
Result = Resultado
Ingredient = Ingrediente
Set time to day = Definir turno para dia
Set time to night = Definir turno para noite
Set home position = Definir posição de casa
Go home = Transportar para Casa
Clear inventory = Limpar Inventário
Give me: = Gerado:
To craft grid: = Para Grade de Artesanato

### group.lua ###
# Logical connective, example: “Any item belonging to the groups foo and bar”
\sand\s =

### waypoints.lua ###
White = Branco
Yellow = Amarelo
Red = Vermelho
Green = Verde
Blue = Azul
Waypoints = Apontador de Direção
Waypoint %d = Apontador de Direção %002
Waypoint active = Apontador de Direção Ativo
Waypoint inactive = Apontador de Direção Inativo
World position = Posição Mundial
Name = Nome
HUD text color = Cor de HUD
Edit waypoint name = Editar Nome de Apontador de Direção
Rename waypoint = Renomear Apontador de Direção
Change color of waypoint display = Mudar cor exibida do Apontador de Direção
Set waypoint to current location = Configurar localização atual do Apontador de Direção
Make waypoint visible = Fazer Apontador de Direção Visível
Make waypoint invisible = Fazer Apontador de Direção Invisível
Disable display of waypoint coordinates = Desativar exibição de coordenadas de Fazer Apontador de Direção
Enable display of waypoint coordinates = Ativar exibição de coordenadas de Fazer Apontador de Direção
Finish editing = Edição Finalizada
Select Waypoint #%d = Seleção de Apontador de Direção #%02d


code:
# Translation by eternal_sorrow

# Template
### bags.lua ###
Bags = Сумки
Bag @1 = Сумка @1
Small Bag = Малая сумка
Medium Bag = Средняя сумка
Large Bag = Большая сумка

### inernal.lua ###
First page = Первая страница
Back three pages = Назад на три страницы
Back one page = Назад на одну страницу
Forward one page = Вперед на одну страницу
Forward three pages = Вперед на три страницы
Last page = Последняя страница
No matching items = Совпадений нет
Page = Страница
%s of %s = %s из %s
Filter = Фильтр
Search = Поиск

### register.lua ###
Can use the creative inventory = Можно использовать инвентарь творческого режима
Home position set to: %s = Дом теперь расположен по коодинатам: %s
Time of day set to 6am = Установлено время 6 утра
You don't have the settime priviledge! = Вам не разрешено устанавливать время!
Time of day set to 9pm = Установлено время 9 вечера
This button has been disabled outside of creative mode to prevent accidental inventory trashing.\nUse the trash slot instead. = Эта кнопка отключена вне творческого режима, чтобы предотвратить случайное уничтожение предметов.\nИспользуйте слот корзины вместо нее.
Inventory cleared! = Инвентарь очищен!
Crafting = Крафт
Trash: = Корзина:
Refill: = Размножить:
Crafting Guide = Книга рецептов
Method: = Способ:
Result: %s = Результат: %s
crafting = крафт
shapeless crafting = бесформенный крафт
cooking = жарка
alloy cooking = приготовление сплавов
Copy to craft grid: = В решетку крафта:
All = Все
Recipe %s of %s = Рецепт %s из %s
Alternate = Следующий
Crafting Grid =  Решетка крафта
Go home = Отправиться домой
Set time to day = День
Set time to night = Ночь
Clear inventory = Очистить инвентарь

### waypoints.lua ###
White = Белый
Yellow = Желтый
Red = Красный
Green = Зелёный
Blue = Синий
Waypoints = Путевые точки
Waypoint active = Путевая точка активна
Waypoint inactive = Путевая точка неактивна
World position = Позиция
Name = Имя
HUD text color = Цвет текста
Edit waypoint name = Редактировать имя путевой точки
Rename waypoint = Переименовать путевую точку
Change color of waypoint display = Изменить цвет путевой точки
Set waypoint to current location = Установить путевую точку в текущем местоположении
Make waypoint visible = Сделать путевую точку видимой
Make waypoint invisible = Сделать путевую точку невидимой
Disable display of waypoint coordinates = Отключить отображение координат путевой точки
Enable display of waypoint coordinates = Включить отображение координат путевой точки
Finish editing = Завершить редактирование
Select Waypoint #%d = Выбрать путевую точку №%d


code:
# Translation by

### api.lua ###
Digging (by chance) =

# Template
### bags.lua ###
Bags =
Bag @1 =
Small Bag =
Medium Bag =
Large Bag =

### inernal.lua ###
First page =
Back three pages =
Back one page =
Forward one page =
Forward three pages =
Last page =
No matching items =
Page =
%s of %s =
Filter =
Search =
Reset search and display everything =

### register.lua ###
Can use the creative inventory =
Forces Unified Inventory to be displayed in Full mode if Lite mode is configured globally =
Home position set to: %s =
Time of day set to 6am =
You don't have the settime priviledge! =
Time of day set to 9pm =
This button has been disabled outside of creative mode to prevent accidental inventory trashing.\nUse the trash slot instead. =
Inventory cleared! =
Crafting =
Digging =
Cooking =
Mixing =
Trash: =
Refill: =
Crafting Guide =
Method: =
Result: %s =
crafting =
shapeless crafting =
cooking =
alloy cooking =
Copy to craft grid: =
All =
Alternate =
Crafting Grid =
Show next recipe =
Show next usage =
Show previous recipe =
Show previous usage =
# Shown for huge crafting recipes; try to keep the line length short and use multiple line breaks as needed
This recipe is too\nlarge to be displayed. =
# %s = group name (e.g. wool)
Any item belonging to the %s group =
# %s = List of “and”-concatenated group names
Any item belonging to the groups %s =
Recipe %d of %d =
Usage %d of %d =
No recipes =
No usages =
Result =
Ingredient =
Set time to day =
Set time to night =
Set home position =
Go home =
Clear inventory =
Give me: =
To craft grid: =

### group.lua ###
# Logical connective, example: “Any item belonging to the groups foo and bar”
\sand\s =

### waypoints.lua ###
White =
Yellow =
Red =
Green =
Blue =
Waypoints =
Waypoint %d =
Waypoint active =
Waypoint inactive =
World position =
Name =
HUD text color =
Edit waypoint name =
Rename waypoint =
Change color of waypoint display =
Set waypoint to current location =
Make waypoint visible =
Make waypoint invisible =
Disable display of waypoint coordinates =
Enable display of waypoint coordinates =
Finish editing =
Select Waypoint #%d =


code:
# Translation by Mahmutelmas06@hotmail.com

# Template
### bags.lua ###
Bags = Çantalarım
Bag @1 = @1. Çanta
Small Bag = Küçük Çanta
Medium Bag = Çanta
Large Bag = Büyük Çanta

### inernal.lua ###
First page = İlk Sayfa
Back three pages = 3 Sayfa Gerile
Back one page =  Geri
Forward one page = İleri
Forward three pages = 3 Sayfa İlerile
Last page = Son Sayfa
No matching items = Eşleşme yok
Page = Sayfa
%s of %s = %s dan %s
Filter = Süzgeç
Search = Ara

### register.lua ###
Can use the creative inventory = Yaratıcı envanteri kullanabilir
Home position set to: %s = Yeni eviniz: %s
Time of day set to 6am = Saat 06:00 olarak ayarlandı
You don't have the settime priviledge = Saati düzenleme yetkiniz yok!
Time of day set to 9pm = Saat 19:00 olarak ayarlandı
This button has been disabled outside of creative mode to prevent accidental inventory trashing.\nUse the trash slot instead. = Yaratıcı modu dışında iken bu tuş kullanılamaz.
Inventory cleared! = Envanter temizlendi!
Crafting = Üretim
Trash: = Çöp
Refill: = Doldur
Crafting Guide = Kılavuz
Method: = Yöntem
Result: %s = Çıktı: %s
crafting = üretim
shapeless crafting = şekilsiz üretim
cooking = pişirme
alloy cooking = karıştırma
Copy to craft grid: = Üretim tablosuna kopyala
All = Tümü
Recipe %s of %s = %s dan %s tarifi
Alternate = Altarnatif
Crafting Grid = Üretim tablosu

### waypoints.lua ###
White = Beyaz
Yellow = Sarı
Red = Kırmızı
Green = Yeşil
Blue = Mavi
Waypoints = Konum Noktaları
Waypoint active = Konum Etkin
Waypoint inactive = Konum Devredışı
World position = Dünya konumu
Name = İsim
HUD text color = Metin rengi
Edit waypoint name = Konum Noktasını Düzenle
Rename waypoint = Konum Noktasını Adlandır
Change color of waypoint display = Konum Gösterge Rengi
Set waypoint to current location = Bulunduğun noktayı işaretle
Make waypoint visible = Konumlar görünür
Make waypoint invisible = Konumlar gözükmez
Disable display of waypoint coordinates = Koordinatları gizle
Enable display of waypoint coordinates = Koordinatları göster
Finish editing = Düzenleme bitti
Select Waypoint #%d = #%d konum noktası seç


code:
name = unified_inventory
author = RealBadAngel
description = Replaces the default inventory and adds a number of features, such as a crafting guide
release = 13
title = Unified Inventory


code:
local S = unified_inventory.gettext
local F = unified_inventory.fgettext

minetest.register_privilege("creative", {
	description = S("Can use the creative inventory"),
	give_to_singleplayer = false,
})

minetest.register_privilege("ui_full", {
	description = S("Forces Unified Inventory to be displayed in Full mode if Lite mode is configured globally"),
	give_to_singleplayer = false,
})

local trash = minetest.create_detached_inventory("trash", {
	--allow_put = function(inv, listname, index, stack, player)
	--	if unified_inventory.is_creative(player:get_player_name()) then
	--		return stack:get_count()
	--	else
	--		return 0
	--	end
	--end,
	on_put = function(inv, listname, index, stack, player)
		inv:set_stack(listname, index, nil)
		local player_name = player:get_player_name()
		minetest.sound_play("trash", {to_player=player_name, gain = 1.0})
	end,
})
trash:set_size("main", 1)

unified_inventory.register_button("craft", {
	type = "image",
	image = "ui_craft_icon.png",
	tooltip = S("Crafting Grid")
})

unified_inventory.register_button("craftguide", {
	type = "image",
	image = "ui_craftguide_icon.png",
	tooltip = S("Crafting Guide")
})

unified_inventory.register_button("home_gui_set", {
	type = "image",
	image = "ui_sethome_icon.png",
	tooltip = S("Set home position"),
	hide_lite=true,
	action = function(player)
		local player_name = player:get_player_name()
		if minetest.check_player_privs(player_name, {home=true}) then
			unified_inventory.set_home(player, player:getpos())
			local home = unified_inventory.home_pos[player_name]
			if home ~= nil then
				minetest.sound_play("dingdong",
						{to_player=player_name, gain = 1.0})
				minetest.chat_send_player(player_name,
					S("Home position set to: %s"):format(minetest.pos_to_string(home)))
			end
		else
			minetest.chat_send_player(player_name,
				S("You don't have the \"home\" privilege!"))
			unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name])
		end
	end,
	condition = function(player)
		return minetest.check_player_privs(player:get_player_name(), {home=true})
	end,
})

unified_inventory.register_button("home_gui_go", {
	type = "image",
	image = "ui_gohome_icon.png",
	tooltip = S("Go home"),
	hide_lite=true,
	action = function(player)
		local player_name = player:get_player_name()
		if minetest.check_player_privs(player_name, {home=true}) then
			minetest.sound_play("teleport",
				{to_player=player:get_player_name(), gain = 1.0})
			unified_inventory.go_home(player)
		else
			minetest.chat_send_player(player_name,
				S("You don't have the \"home\" privilege!"))
			unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name])
		end
	end,
	condition = function(player)
		return minetest.check_player_privs(player:get_player_name(), {home=true})
	end,
})

unified_inventory.register_button("misc_set_day", {
	type = "image",
	image = "ui_sun_icon.png",
	tooltip = S("Set time to day"),
	hide_lite=true,
	action = function(player)
		local player_name = player:get_player_name()
		if minetest.check_player_privs(player_name, {settime=true}) then
			minetest.sound_play("birds",
					{to_player=player_name, gain = 1.0})
			minetest.set_timeofday((6000 % 24000) / 24000)
			minetest.chat_send_player(player_name,
				S("Time of day set to 6am"))
		else
			minetest.chat_send_player(player_name,
				S("You don't have the settime privilege!"))
			unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name])
		end
	end,
	condition = function(player)
		return minetest.check_player_privs(player:get_player_name(), {settime=true})
	end,
})

unified_inventory.register_button("misc_set_night", {
	type = "image",
	image = "ui_moon_icon.png",
	tooltip = S("Set time to night"),
	hide_lite=true,
	action = function(player)
		local player_name = player:get_player_name()
		if minetest.check_player_privs(player_name, {settime=true}) then
			minetest.sound_play("owl",
					{to_player=player_name, gain = 1.0})
			minetest.set_timeofday((21000 % 24000) / 24000)
			minetest.chat_send_player(player_name,
					S("Time of day set to 9pm"))
		else
			minetest.chat_send_player(player_name,
					S("You don't have the settime privilege!"))
			unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name])
		end
	end,
	condition = function(player)
		return minetest.check_player_privs(player:get_player_name(), {settime=true})
	end,
})

unified_inventory.register_button("clear_inv", {
	type = "image",
	image = "ui_trash_icon.png",
	tooltip = S("Clear inventory"),
	action = function(player)
		local player_name = player:get_player_name()
		if not unified_inventory.is_creative(player_name) then
			minetest.chat_send_player(player_name,
					S("This button has been disabled outside"
					.." of creative mode to prevent"
					.." accidental inventory trashing."
					.."\nUse the trash slot instead."))
			unified_inventory.set_inventory_formspec(player, unified_inventory.current_page[player_name])
			return
		end
		player:get_inventory():set_list("main", {})
		minetest.chat_send_player(player_name, S('Inventory cleared!'))
		minetest.sound_play("trash_all",
				{to_player=player_name, gain = 1.0})
	end,
	condition = function(player)
		return unified_inventory.is_creative(player:get_player_name())
	end,
})

unified_inventory.register_page("craft", {
	get_formspec = function(player, perplayer_formspec)

		local formspecy = perplayer_formspec.formspec_y
		local formheadery =  perplayer_formspec.form_header_y

		local player_name = player:get_player_name()
		local formspec = "background[2,"..formspecy..";6,3;ui_crafting_form.png]"
		formspec = formspec.."background[0,"..(formspecy + 3.5)..";8,4;ui_main_inventory.png]"
		formspec = formspec.."label[0,"..formheadery..";" ..F("Crafting").."]"
		formspec = formspec.."listcolors[#00000000;#00000000]"
		formspec = formspec.."list[current_player;craftpreview;6,"..formspecy..";1,1;]"
		formspec = formspec.."list[current_player;craft;2,"..formspecy..";3,3;]"
		if unified_inventory.trash_enabled or unified_inventory.is_creative(player_name) or minetest.get_player_privs(player_name).give then
			formspec = formspec.."label[7,"..(formspecy + 1.5)..";" .. F("Trash:") .. "]"
			formspec = formspec.."background[7,"..(formspecy + 2)..";1,1;ui_single_slot.png]"
			formspec = formspec.."list[detached:trash;main;7,"..(formspecy + 2)..";1,1;]"
		end
		formspec = formspec.."listring[current_name;craft]"
		formspec = formspec.."listring[current_player;main]"
		if unified_inventory.is_creative(player_name) then
			formspec = formspec.."label[0,"..(formspecy + 1.5)..";" .. F("Refill:") .. "]"
			formspec = formspec.."list[detached:"..minetest.formspec_escape(player_name).."refill;main;0,"..(formspecy +2)..";1,1;]"
		end
		return {formspec=formspec}
	end,
})

-- stack_image_button(): generate a form button displaying a stack of items
--
-- The specified item may be a group.  In that case, the group will be
-- represented by some item in the group, along with a flag indicating
-- that it's a group.  If the group contains only one item, it will be
-- treated as if that item had been specified directly.

local function stack_image_button(x, y, w, h, buttonname_prefix, item)
	local name = item:get_name()
	local count = item:get_count()
	local show_is_group = false
	local displayitem = name.." "..count
	local selectitem = name
	if name:sub(1, 6) == "group:" then
		local group_name = name:sub(7)
		local group_item = unified_inventory.get_group_item(group_name)
		show_is_group = not group_item.sole
		displayitem = group_item.item or "unknown"
		selectitem = group_item.sole and displayitem or name
	end
	local label = show_is_group and "G" or ""
	local buttonname = minetest.formspec_escape(buttonname_prefix..unified_inventory.mangle_for_formspec(selectitem))
	local button = string.format("item_image_button[%f,%f;%f,%f;%s;%s;%s]",
			x, y, w, h,
			minetest.formspec_escape(displayitem), buttonname, label)
	if show_is_group then
		local groupstring, andcount = unified_inventory.extract_groupnames(name)
		local grouptip
		if andcount == 1 then
			grouptip = string.format(S("Any item belonging to the %s group"), groupstring)
		elseif andcount > 1 then
			grouptip = string.format(S("Any item belonging to the groups %s"), groupstring)
		end
		grouptip = minetest.formspec_escape(grouptip)
		if andcount >= 1 then
			button = button  .. string.format("tooltip[%s;%s]", buttonname, grouptip)
		end
	end
	return button
end

local recipe_text = {
	recipe = "Recipe %d of %d",
	usage = "Usage %d of %d",
}
local no_recipe_text = {
	recipe = "No recipes",
	usage = "No usages",
}
local role_text = {
	recipe = "Result",
	usage = "Ingredient",
}
local next_alt_text = {
	recipe = "Show next recipe",
	usage = "Show next usage",
}
local prev_alt_text = {
	recipe = "Show previous recipe",
	usage = "Show previous usage",
}
local other_dir = {
	recipe = "usage",
	usage = "recipe",
}

unified_inventory.register_page("craftguide", {
	get_formspec = function(player, perplayer_formspec)

		local formspecy =    perplayer_formspec.formspec_y
		local formheadery =  perplayer_formspec.form_header_y
		local craftresultx = perplayer_formspec.craft_result_x
		local craftresulty = perplayer_formspec.craft_result_y

		local player_name = player:get_player_name()
		local player_privs = minetest.get_player_privs(player_name)
		local formspec = ""
		formspec = formspec.."background[0,"..(formspecy + 3.5)..";8,4;ui_main_inventory.png]"
		formspec = formspec.."label[0,"..formheadery..";" .. F("Crafting Guide") .. "]"
		formspec = formspec.."listcolors[#00000000;#00000000]"
		local item_name = unified_inventory.current_item[player_name]
		if not item_name then return {formspec=formspec} end
		local item_name_shown
		if minetest.registered_items[item_name] and minetest.registered_items[item_name].description then
			item_name_shown = string.format(S("%s (%s)"), minetest.registered_items[item_name].description, item_name)
		else
			item_name_shown = item_name
		end

		local dir = unified_inventory.current_craft_direction[player_name]
		local rdir
		if dir == "recipe" then rdir = "usage" end
		if dir == "usage" then rdir = "recipe" end
		local crafts = unified_inventory.crafts_for[dir][item_name]
		local alternate = unified_inventory.alternate[player_name]
		local alternates, craft
		if crafts ~= nil and #crafts > 0 then
			alternates = #crafts
			craft = crafts[alternate]
		end

		formspec = formspec.."background[0.5,"..(formspecy + 0.2)..";8,3;ui_craftguide_form.png]"
		formspec = formspec.."textarea["..craftresultx..","..craftresulty
                           ..";10,1;;"..minetest.formspec_escape(F(role_text[dir])..": "..item_name_shown)..";]"
		formspec = formspec..stack_image_button(0, formspecy, 1.1, 1.1, "item_button_"
		                   .. rdir .. "_", ItemStack(item_name))

		if not craft then
			formspec = formspec.."label[5.5,"..(formspecy + 2.35)..";"
			                   ..minetest.formspec_escape(F(no_recipe_text[dir])).."]"
			local no_pos = dir == "recipe" and 4.5 or 6.5
			local item_pos = dir == "recipe" and 6.5 or 4.5
			formspec = formspec.."image["..no_pos..","..formspecy..";1.1,1.1;ui_no.png]"
			formspec = formspec..stack_image_button(item_pos, formspecy, 1.1, 1.1, "item_button_"
			                   ..other_dir[dir].."_", ItemStack(item_name))
			if player_privs.give == true or player_privs.creative == true or minetest.setting_getbool("creative_mode") == true then
				formspec = formspec.."label[0,"..(formspecy + 2.10)..";" .. F("Give me:") .. "]"
						.."button[0,  "..(formspecy + 2.7)..";0.6,0.5;craftguide_giveme_1;1]"
						.."button[0.6,"..(formspecy + 2.7)..";0.7,0.5;craftguide_giveme_10;10]"
						.."button[1.3,"..(formspecy + 2.7)..";0.8,0.5;craftguide_giveme_99;99]"
			end
			return {formspec = formspec}
		end

		local craft_type = unified_inventory.registered_craft_types[craft.type] or
				unified_inventory.craft_type_defaults(craft.type, {})
		if craft_type.icon then
			formspec = formspec..string.format(" image[%f,%f;%f,%f;%s]",5.7,(formspecy + 0.05),0.5,0.5,craft_type.icon)
		end
		formspec = formspec.."label[5.5,"..(formspecy + 1)..";" .. minetest.formspec_escape(craft_type.description).."]"
		formspec = formspec..stack_image_button(6.5, formspecy, 1.1, 1.1, "item_button_usage_", ItemStack(craft.output))
		local display_size = craft_type.dynamic_display_size and craft_type.dynamic_display_size(craft) or { width = craft_type.width, height = craft_type.height }
		local craft_width = craft_type.get_shaped_craft_width and craft_type.get_shaped_craft_width(craft) or display_size.width

		-- This keeps recipes aligned to the right,
		-- so that they're close to the arrow.
		local xoffset = 5.5
		-- Offset factor for crafting grids with side length > 4
		local of = (3/math.max(3, math.max(display_size.width, display_size.height)))
		local od = 0
		-- Minimum grid size at which size optimazation measures kick in
		local mini_craft_size = 6
		if display_size.width >= mini_craft_size then
			od = math.max(1, display_size.width - 2)
			xoffset = xoffset - 0.1
		end
		-- Size modifier factor
		local sf = math.min(1, of * (1.05 + 0.05*od))
		-- Button size
		local bsize_h = 1.1 * sf
		local bsize_w = bsize_h
		if display_size.width >= mini_craft_size then
			bsize_w = 1.175 * sf
		end
		if (bsize_h > 0.35 and display_size.width) then
		for y = 1, display_size.height do
		for x = 1, display_size.width do
			local item
			if craft and x <= craft_width then
				item = craft.items[(y-1) * craft_width + x]
			end
			-- Flipped x, used to build formspec buttons from right to left
			local fx = display_size.width - (x-1)
			-- x offset, y offset
			local xof = (fx-1) * of + of
			local yof = (y-1) * of + 1
			if item then
				formspec = formspec..stack_image_button(
						xoffset - xof, formspecy - 1 + yof, bsize_w, bsize_h,
						"item_button_recipe_",
						ItemStack(item))
			else
				-- Fake buttons just to make grid
				formspec = formspec.."image_button["
					..tostring(xoffset - xof)..","..tostring(formspecy - 1 + yof)
					..";"..bsize_w..","..bsize_h..";ui_blank_image.png;;]"
			end
		end
		end
		else
			-- Error
			formspec = formspec.."label["
				..tostring(2)..","..tostring(formspecy)
				..";"..minetest.formspec_escape(S("This recipe is too\nlarge to be displayed.")).."]"
		end

		if craft_type.uses_crafting_grid and display_size.width <= 3 then
			formspec = formspec.."label[0,"..(formspecy + 0.9)..";" .. F("To craft grid:") .. "]"
					.."button[0,  "..(formspecy + 1.5)..";0.6,0.5;craftguide_craft_1;1]"
					.."button[0.6,"..(formspecy + 1.5)..";0.7,0.5;craftguide_craft_10;10]"
					.."button[1.3,"..(formspecy + 1.5)..";0.8,0.5;craftguide_craft_max;" .. F("All") .. "]"
		end
		if player_privs.give == true or player_privs.creative == true or minetest.setting_getbool("creative_mode") == true then
			formspec = formspec.."label[0,"..(formspecy + 2.1)..";" .. F("Give me:") .. "]"
					.."button[0,  "..(formspecy + 2.7)..";0.6,0.5;craftguide_giveme_1;1]"
					.."button[0.6,"..(formspecy + 2.7)..";0.7,0.5;craftguide_giveme_10;10]"
					.."button[1.3,"..(formspecy + 2.7)..";0.8,0.5;craftguide_giveme_99;99]"
		end

		if alternates and alternates > 1 then
			formspec = formspec.."label[5.5,"..(formspecy + 1.6)..";"
					..string.format(F(recipe_text[dir]), alternate, alternates).."]"
					.."image_button[5.5,"..(formspecy + 2)..";1,1;ui_left_icon.png;alternate_prev;]"
					.."image_button[6.5,"..(formspecy + 2)..";1,1;ui_right_icon.png;alternate;]"
					.."tooltip[alternate_prev;"..F(prev_alt_text[dir]).."]"
					.."tooltip[alternate;"..F(next_alt_text[dir]).."]"
		end
		return {formspec = formspec}
	end,
})

local function craftguide_giveme(player, formname, fields)
	local amount
	for k, v in pairs(fields) do
		amount = k:match("craftguide_giveme_(.*)")
		if amount then break end
	end
	if not amount then return end

	amount = tonumber(amount)
	if amount == 0 then return end

	local player_name = player:get_player_name()

	local output = unified_inventory.current_item[player_name]
	if (not output) or (output == "") then return end

	local player_inv = player:get_inventory()

	player_inv:add_item("main", {name = output, count = amount})
end

-- tells if an item can be moved and returns an index if so
local function item_fits(player_inv, craft_item, needed_item)
	local need_group = string.sub(needed_item, 1, 6) == "group:"
	if need_group then
		need_group = string.sub(needed_item, 7)
	end
	if craft_item
	and not craft_item:is_empty() then
		local ciname = craft_item:get_name()

		-- abort if the item there isn't usable
		if ciname ~= needed_item
		and not need_group then
			return
		end

		-- abort if no item fits onto it
		if craft_item:get_count() >= craft_item:get_definition().stack_max then
			return
		end

		-- use the item there if it's in the right group and a group item is needed
		if need_group then
			if minetest.get_item_group(ciname, need_group) == 0 then
				return
			end
			needed_item = ciname
			need_group = false
		end
	end

	if need_group then
		-- search an item of the specific group
		for i,item in pairs(player_inv:get_list("main")) do
			if not item:is_empty()
			and minetest.get_item_group(item:get_name(), need_group) > 0 then
				return i
			end
		end

		-- no index found
		return
	end

	-- search an item with a the name needed_item
	for i,item in pairs(player_inv:get_list("main")) do
		if not item:is_empty()
		and item:get_name() == needed_item then
			return i
		end
	end

	-- no index found
end

-- modifies the player inventory and returns the changed craft_item if possible
local function move_item(player_inv, craft_item, needed_item)
	local stackid = item_fits(player_inv, craft_item, needed_item)
	if not stackid then
		return
	end
	local wanted_stack = player_inv:get_stack("main", stackid)
	local taken_item = wanted_stack:take_item()
	player_inv:set_stack("main", stackid, wanted_stack)

	if not craft_item
	or craft_item:is_empty() then
		return taken_item
	end

	craft_item:add_item(taken_item)
	return craft_item
end

local function craftguide_craft(player, formname, fields)
	local amount
	for k, v in pairs(fields) do
		amount = k:match("craftguide_craft_(.*)")
		if amount then break end
	end
	if not amount then return end
	local player_name = player:get_player_name()

	local output = unified_inventory.current_item[player_name]
	if (not output) or (output == "") then return end

	local player_inv = player:get_inventory()

	local crafts = unified_inventory.crafts_for[unified_inventory.current_craft_direction[player_name]][output]
	if (not crafts) or (#crafts == 0) then return end

	local alternate = unified_inventory.alternate[player_name]

	local craft = crafts[alternate]
	if craft.width > 3 then return end

	local needed = craft.items

	local craft_list = player_inv:get_list("craft")

	local width = craft.width
	if width == 0 then
		-- Shapeless recipe
		width = 3
	end

	amount = tonumber(amount) or 99
	--[[
	if amount == "max" then
		amount = 99 -- Arbitrary; need better way to do this.
	else
		amount = tonumber(amount)
	end--]]

	for iter = 1, amount do
		local index = 1
		for y = 1, 3 do
			for x = 1, width do
				local needed_item = needed[index]
				if needed_item then
					local craft_index = ((y - 1) * 3) + x
					local craft_item = craft_list[craft_index]
					local newitem = move_item(player_inv, craft_item, needed_item)
					if newitem then
						craft_list[craft_index] = newitem
					end
				end
				index = index + 1
			end
		end
	end

	player_inv:set_list("craft", craft_list)

	unified_inventory.set_inventory_formspec(player, "craft")
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
	for k, v in pairs(fields) do
		if k:match("craftguide_craft_") then
			craftguide_craft(player, formname, fields)
			return
		end
		if k:match("craftguide_giveme_") then
			craftguide_giveme(player, formname, fields)
			return
		end
	end
end)


code:
#Enabling lite mode enables a smaller and simpler version of the Unified
#Inventory, optimized for small displays.
unified_inventory_lite (Lite mode) bool false

#If enabled, bags will be made available which can be used to extend
#inventory storage size.
unified_inventory_bags (Enable bags) bool true

#If enabled, the trash slot can be used by those without both creative
#and the give privilege.
unified_inventory_trash (Enable trash) bool true


code:
local S = unified_inventory.gettext
local F = unified_inventory.fgettext

local hud_colors = {
	{"#FFFFFF", 0xFFFFFF, S("White")},
	{"#DBBB00", 0xf1d32c, S("Yellow")},
	{"#DD0000", 0xDD0000, S("Red")},
	{"#2cf136", 0x2cf136, S("Green")},
	{"#2c4df1", 0x2c4df1, S("Blue")},
}

local hud_colors_max = #hud_colors

-- Stores temporary player data (persists until player leaves)
local waypoints_temp = {}

unified_inventory.register_page("waypoints", {
	get_formspec = function(player)
		local player_name = player:get_player_name()

		-- build a "fake" temp entry if the server took too long
		-- during sign-on and returned an empty entry
		if not waypoints_temp[player_name] then waypoints_temp[player_name] = {hud = 1} end

		local waypoints = datastorage.get(player_name, "waypoints")
		local formspec = "background[0,4.5;8,4;ui_main_inventory.png]" ..
			"image[0,0;1,1;ui_waypoints_icon.png]" ..
			"label[1,0;" .. F("Waypoints") .. "]"

		-- Tabs buttons:
		for i = 1, 5, 1 do
			formspec = formspec ..
				"image_button[0.0," .. 0.2 + i * 0.7 .. ";.8,.8;" ..
				(i == waypoints.selected and "ui_blue_icon_background.png^" or "") ..
				"ui_" .. i .. "_icon.png;" ..
				"select_waypoint" .. i .. ";]" ..
				"tooltip[select_waypoint" .. i .. ";"
					.. (S("Select Waypoint #%d"):format(i)).."]"
		end

		local i = waypoints.selected or 1
		local waypoint = waypoints[i] or {}
		local temp = waypoints_temp[player_name][i] or {}
		local default_name = string.format(S("Waypoint %d"), i)

		-- Main buttons:
		formspec = formspec ..
			"image_button[4.5,3.7;.8,.8;"..
			"ui_waypoint_set_icon.png;"..
			"set_waypoint"..i..";]"..
			"tooltip[set_waypoint" .. i .. ";"
				.. F("Set waypoint to current location").."]"

		formspec = formspec ..
			"image_button[5.2,3.7;.8,.8;"..
			(waypoint.active and "ui_on_icon.png" or "ui_off_icon.png")..";"..
			"toggle_waypoint"..i..";]"..
			"tooltip[toggle_waypoint" .. i .. ";"
				.. F("Make waypoint "
					..(waypoint.active and "invisible" or "visible")).."]"

		formspec = formspec ..
			"image_button[5.9,3.7;.8,.8;"..
			(waypoint.display_pos and "ui_green_icon_background.png" or "ui_red_icon_background.png").."^ui_xyz_icon.png;"..
			"toggle_display_pos" .. i .. ";]"..
			"tooltip[toggle_display_pos" .. i .. ";"
				.. F((waypoint.display_pos and "Disable" or "Enable")
					.." display of waypoint coordinates").."]"

		formspec = formspec ..
			"image_button[6.6,3.7;.8,.8;"..
			"ui_circular_arrows_icon.png;"..
			"toggle_color"..i..";]"..
			"tooltip[toggle_color" .. i .. ";"
				.. F("Change color of waypoint display").."]"

		formspec = formspec ..
			"image_button[7.3,3.7;.8,.8;"..
			"ui_pencil_icon.png;"..
			"rename_waypoint"..i..";]"..
			"tooltip[rename_waypoint" .. i .. ";"
				.. F("Edit waypoint name").."]"

		-- Waypoint's info:
		if waypoint.active then
			formspec = formspec .. 	"label[1,0.8;"..F("Waypoint active").."]"
		else
			formspec = formspec .. 	"label[1,0.8;"..F("Waypoint inactive").."]"
		end

		if temp.edit then
			formspec = formspec ..
				"field[1.3,3.2;6,.8;rename_box" .. i .. ";;"
				..(waypoint.name or default_name).."]" ..
				"image_button[7.3,2.9;.8,.8;"..
				"ui_ok_icon.png;"..
				"confirm_rename"..i.. ";]"..
				"tooltip[confirm_rename" .. i .. ";"
					.. F("Finish editing").."]"
		end

		formspec = formspec .. "label[1,1.3;"..F("World position")..": " ..
			minetest.pos_to_string(waypoint.world_pos or vector.new()) .. "]" ..
			"label[1,1.8;"..F("Name")..": ".. (waypoint.name or default_name) .. "]" ..
			"label[1,2.3;"..F("HUD text color")..": " ..
			hud_colors[waypoint.color or 1][3] .. "]"

		return {formspec=formspec}
	end,
})

unified_inventory.register_button("waypoints", {
	type = "image",
	image = "ui_waypoints_icon.png",
	tooltip = S("Waypoints"),
	hide_lite=true
})

local function update_hud(player, waypoints, temp, i)
	local waypoint = waypoints[i]
	if not waypoint then return end
	temp[i] = temp[i] or {}
	temp = temp[i]
	local pos = waypoint.world_pos or vector.new()
	local name
	if waypoint.display_pos then
		name = minetest.pos_to_string(pos)
		if waypoint.name then
			name = name..", "..waypoint.name
		end
	else
		name = waypoint.name or "Waypoint "..i
	end
	if temp.hud then
		player:hud_remove(temp.hud)
	end
	if waypoint.active then
		temp.hud = player:hud_add({
			hud_elem_type = "waypoint",
			number = hud_colors[waypoint.color or 1][2] ,
			name = name,
			text = "m",
			world_pos = pos
		})
	else
		temp.hud = nil
	end
end

minetest.register_on_player_receive_fields(function(player, formname, fields)
	if formname ~= "" then return end

	local player_name = player:get_player_name()
	local update_formspec = false
	local need_update_hud = false
	local hit = false

	local waypoints = datastorage.get(player_name, "waypoints")
	local temp = waypoints_temp[player_name]
	for i = 1, 5, 1 do
		if fields["select_waypoint"..i] then
			hit = true
			waypoints.selected = i
			update_formspec = true
		end

		if fields["toggle_waypoint"..i] then
			hit = true
			waypoints[i] = waypoints[i] or {}
			waypoints[i].active = not (waypoints[i].active)
			need_update_hud = true
			update_formspec = true
		end

		if fields["set_waypoint"..i] then
			hit = true
			local pos = player:getpos()
			pos.x = math.floor(pos.x)
			pos.y = math.floor(pos.y)
			pos.z = math.floor(pos.z)
			waypoints[i] = waypoints[i] or {}
			waypoints[i].world_pos = pos
			need_update_hud = true
			update_formspec = true
		end

		if fields["rename_waypoint"..i] then
			hit = true
			temp[i] = temp[i] or {}
			temp[i].edit = true
			update_formspec = true
		end

		if fields["toggle_display_pos"..i] then
			hit = true
			waypoints[i] = waypoints[i] or {}
			waypoints[i].display_pos = not waypoints[i].display_pos
			need_update_hud = true
			update_formspec = true
		end

		if fields["toggle_color"..i] then
			hit = true
			waypoints[i] = waypoints[i] or {}
			local color = waypoints[i].color or 1
			color = color + 1
			if color > hud_colors_max then
				color = 1
			end
			waypoints[i].color = color
			need_update_hud = true
			update_formspec = true
		end

		if fields["confirm_rename"..i] then
			hit = true
			waypoints[i] = waypoints[i] or {}
			temp[i].edit = false
			waypoints[i].name = fields["rename_box"..i]
			need_update_hud = true
			update_formspec = true
		end
		if need_update_hud then
			update_hud(player, waypoints, temp, i)
		end
		if update_formspec then
			unified_inventory.set_inventory_formspec(player, "waypoints")
		end
		if hit then return end
	end
end)

minetest.register_on_joinplayer(function(player)
	local player_name = player:get_player_name()
	local waypoints = datastorage.get(player_name, "waypoints")
	local temp = {}
	waypoints_temp[player_name] = temp
	for i = 1, 5 do
		update_hud(player, waypoints, temp, i)
	end
end)

minetest.register_on_leaveplayer(function(player)
	waypoints_temp[player:get_player_name()] = nil
end)


code:
# Vps Blocker

This mod checks the ips of the player for vpns, proxys, or other hosting services. These clients will be blocked
It gets the information from proxy-check websites like iphub.info

Installing
----------

First you need to add vps_blocker to secure.http_mods to allow http requests.

Set the kick message:
vps_kick_message = "You are using a proxy, vpn or other hosting services, please disable them to play on this server."

Commands
--------

Use /vps_wl (add or remove) (name) to allow people using vps.

Checkers
--------

Paste the keys to the minetest.conf

###### iphub.info

To use iphub.info you need to register there(recommend)
You get 1000 requests/day for free
iphub_key = 123

###### getipintel.net

You must set an own contact email for getipintel.net
Please first go to the webpage and read what it's used for!
getipintel_contact = example@example.com

###### proxycheck.io

For a higher request limit 1000 instead of 100 requests and dashboard register at there webpage
proxycheck_key = 1-2-3-4

License
-------

The idea for the mod is taken from https://github.com/red-001/block_vps but it's a complete redo of it.

Created by [Lejo](https://github.com/Lejo1)
License: MIT


code:
--  Default proxy and vpn checker websites
--  The best checker should be the first registered!

--  Iphub.info

local iphub_key = minetest.settings:get("iphub_key")
if iphub_key then
  local check = {}
  function check.getreq(ip)
    local req = {
      ["url"] = "http://v2.api.iphub.info/ip/"..ip,
      ["extra_headers"] = {"X-Key: "..iphub_key}
    }
    local callback = function(result)
      if result.code == 429 then --  Iphub request limit reached!
        check.active = false
        return nil, "IPhub Limit reached!"
      end
      local data = minetest.parse_json(result.data)
      if result.completed and result.succeeded and data and data.block then --  Correct request
        if data.block == 1 then
          return false
        else return true
        end
      else return nil, "Incorrect iphub request!"
      end
    end
    return req, callback
  end

  vps_blocker.register_checker(check)
end

--  Getipintel.net

local contact = minetest.settings:get("getipintel_contact")
if contact then
  local check = {}
  function check.getreq(ip)
    local req = {
      ["url"] = "https://check.getipintel.net/check.php?contact="..contact.."&ip="..ip
    }
    local callback = function(result)
      if result.code == 429 then --  Iphub request limit reached!
        check.active = false
        return nil, "Getipintel Limit reached!"
      end
      local data = tonumber(result.data)
      if result.completed and result.succeeded and result.code == 200 and data then --  Correct request
        if data > 0.99 then
          return false
        else return true
        end
      else return nil, "Incorrect getipintel request!"
      end
    end
    return req, callback
  end

  vps_blocker.register_checker(check)
end

--  Proxycheck.io

do
  local proxycheck_key = minetest.settings:get("proxycheck_key")
  local check = {}
  function check.getreq(ip)
    local req = {
      ["url"] = "http://proxycheck.io/v2/"..ip.."?vpn=1"
    }
    if proxycheck_key then
      req.url = req.url.."&key="..proxycheck_key
    end
    local callback = function(result)
      local data = minetest.parse_json(result.data)
      if result.completed and result.succeeded and data then --  Correct request
        if data.status == "ok" or data.status == "warning" and data[ip] and data[ip].proxy then
          if data[ip].proxy == "yes" then
            return false
          else return true
          end
        elseif data.status == "denied" and string.find(data.message, "exhausted") then
          check.active = false
          return nil, "Proxycheck Limit reached!"
        else return nil, (data.status or "error") .. ": "..(data.message or "Bad request-result!")
        end
      else return nil, "Incorrect iphub request!"
      end
    end
    return req, callback
  end

  vps_blocker.register_checker(check)
end


code:
--  Vps Blocker

local http = minetest.request_http_api()
assert(http ~= nil, "You need to add vps_blocker to secure.http_mods")

local kick_message = minetest.settings:get("vps_kick_message") or "You are using a proxy, vpn or other hosting services, please disable them to play on this server."

vps_blocker = {}
local cache = {}
local storage = minetest.get_mod_storage()
--[[
cache of ip == nil not checked yet
                 == 1 checked allow
                 == 2 checked deny
]]

local checkers = {}

--[[
vps_blocker.register_check(check):
passed check is a array:

getreq(ip) return req, callback or nil, err for failed

callback func will get the result of the req and is supposed
to return true for allow and false for denying the client
nil, err for failed requests

active = true
If the checker is currently working is true by default

The checker can store any other data inside this table.
]]

function vps_blocker.register_checker(check)
  assert(type(check) == "table")
  check.active = true
  assert(type(check.getreq) == "function")
  --  Dummy testing function:
  local req, call = check.getreq("0.0.0.0")
  assert(type(req) == "table")
  assert(type(req.url == "string"))
  assert(type(call) == "function")
  table.insert(checkers, check)
end

--  Load checkers
dofile(minetest.get_modpath(minetest.get_current_modname()).. "/checker.lua")

--  Add the main ipcheckup function
local function check_ip(name, ip, hash)
  --  Loop throught the list of checkers and use one
  local checked = false
  for _, check in pairs(checkers) do
    if check.active then
      local req, call = check.getreq(ip)
      if req then
        function callback(result)
          local pass, err = call(result)
          if pass then
            cache[hash] = 1
            minetest.log("action", "vps_blocker: Passing good-ip-player "..name.." ["..ip.."]")
          elseif pass == false then
            cache[hash] = 2
            minetest.log("action", "vps_blocker: Kicking bad-ip-player "..name.." ["..ip.."]")
          else minetest.log("error", "vps_blocker: Callback-Error "..err.." while checking "..name.." ["..ip.."]!")
          end
        end
        http.fetch(req, callback)
        checked = true
        break
      else minetest.log("error", "vps_blocker: Checker failed to create requests for "..name.." ["..ip.."]!")
      end
    end
  end
  --  Report error if no working was found
  if not checked then
    minetest.log("error", "vps_blocker: No working checker found!")
  end
end

--  Add a function which handels what do do(check, kick, nth...)
function vps_blocker.handle_player(name, ip)
  if not ip or not name then
    return
  end
  local iphash = minetest.sha1(ip)
  if not iphash then
    return
  end
  if cache[iphash] == 1 or storage:get_int(name) == 1 then
    return
  end
  if not cache[iphash] then
    check_ip(name, ip, iphash)
    return
  end
  if cache[iphash] == 2 then
    local player = minetest.get_player_by_name(name)
    if player then
      --  Kick after a server step, to prevent other on_joinplayer to crash
      minetest.after(0, function()
        minetest.kick_player(name, kick_message)
      end)
    else return kick_message
    end
  end
end

--  Do handle_player on prejoin and normal join
minetest.register_on_prejoinplayer(vps_blocker.handle_player)
minetest.register_on_joinplayer(function(player)
  local name = player:get_player_name()
  local ip = minetest.get_player_ip(name)
  vps_blocker.handle_player(name, ip)
end)

--  Add a command to whitelist players
minetest.register_chatcommand("vps_wl", {
  description = "Allow a player to use vps services.",
  params = "<add or remove> <name>",
  privs = {server=true},
  func = function(name, params)
    local p = string.split(params, " ")
    if p[1] == "add" then
      storage:set_int(p[2], 1)
      return true, "Added "..p[2].." to the whitelist."
    elseif p[1] == "remove" then
      storage:set_int(p[2], 0)
      return true, "Removed "..p[2].." from the whitelist."
    else return false, "Invalid Input"
    end
  end
})


code:
name = vps_blocker
description = Block players using vpns, proxys or other hosting services


code:
<!DOCTYPE html>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Play MineTest Server</title>

    <meta name="author" content="Conner Vieira">
    <meta name="title" content="Play MineTest Server">
    <meta name="description" content="Play MineTest is the perfect MineTest server, whether you're looking for a friendly community to learn more about the game, or you're already an experienced player and are looking to share your talents!">

Webpage created by V0LT

Play MineTest server

The best place to enjoy MineTest!

MineTest is an open source clone of Minecraft, with a great modding community, and lots of fun features. If you're a new player, it might seem like a daunting task to learn how to play. Thats where the Play MineTest Server comes in! Get into MineTest with a friendly community, and a strong selection of mods that make the experience much richer and more engaging.

If you're an experienced MineTest player, the Play MineTest server is also a great place for you to share your talent as well! Build intricate machines using the Technic and Mesecon mods, or create intricate structures using the hundreds of available blocks.

The Play MineTest server is entirely open source. Everything from the game itself, to every mod, and even to game's Matrix community are open source.

How To Join

Joining the Play MineTest server is simple!

Download MineTest 5.3 if you haven't already

Open MineTest

Click the 'Join Game' tab

In the side bar on the right, enter the Address as play.playminetest.com and the Port as 30000

Enter a username and password to use on the Play MineTest Server.

Join the Play Minetest Server group on Matrix

If you're a Matrix user, you can join the official Play MineTest server Matrix chat here!

Mods List:

bouncy_bed

advancedban

mobs_monster

skinsdb

toolranks

tpr

toolranks_extras

moreores

3d_armor_ip

3d_armor_sfinv

morelights_modern

mob_horse

chatlog

protector

travelnet

3d_armor

wieldview

morelights_extras

shields

areas

3d_armor_stand

treecapitator

gates_long

mobs_animal

stairs

mod_mail

morelights

morelights_vintage

future_ban

3d_armor_ui

mobs

reportlist

bonemeal

spawn_command

compresser

ts_furniture

vps_blocker

signs

itemframes

cleaner

teleport_potion

unified_inventory

inventory_bags

technic_plus

mesecons

first_hour

restart_gift

lagcheck

mesecons_debug

farming_regen

carpets

craftabledirtwithgrass

digilines

technic_addons

Support

The Play MineTest server is run completely free, and without ads or any other monetization. It depends on donations to stay up and running. Here you can support the Play MineTest server!

BTC: bc1qsx0upxv2pha7aayqwm3g323tej0nv84zz6tpkv

ETH: 0x4Ca0296d759394d610e7565D7d024a75c98cb0Be

LTC: LMhf4BS4BtupwY6qWHUp7KyvZzuoSvKw8a

PayPal: https://paypal.me/rickyt12

This website was created at no charge by V0LT, who is unaffiliated with the Play MineTest server. The owner of the Play MineTest server receives 100% of all donations above. If you're interested in supporting V0LT as well, you can learn more on the official V0LT website.


code:
# In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
#  * .env                contains default values for the environment variables needed by the app
#  * .env.local          uncommitted file with local overrides
#  * .env.$APP_ENV       committed environment-specific defaults
#  * .env.$APP_ENV.local uncommitted environment-specific overrides
#
# Real environment variables win over .env files.
#
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration

###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=6a9ac3a09c73230107373e8e0b71e0a3
#TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
#TRUSTED_HOSTS='^(localhost|example\.com)$'
###< symfony/framework-bundle ###

###> symfony/mailer ###
# MAILER_DSN=smtp://localhost
###< symfony/mailer ###

###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11&charset=utf8"
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
DATABASE_URL=postgresql://postgres:foobar@postgres:5432/social
#?serverVersion=11&charset=utf8
###< doctrine/doctrine-bundle ###

SHELL_VERBOSITY=3
###> symfony/messenger ###
# Choose one of the transports below
# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
MESSENGER_TRANSPORT_DSN_HIGH=doctrine://default?queue_name=high
MESSENGER_TRANSPORT_DSN_LOW=doctrine://default?queue_name=low
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
###< symfony/messenger ###

repo_name: ksuudilla/gnu

path: .env


code:
# define your env variables for the test env here
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther


code:
###> symfony/framework-bundle ###
/.env.local
/.env.local.php
/.env.*.local
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/var/
/vendor/
###< symfony/framework-bundle ###

###> symfony/phpunit-bridge ###
.phpunit
.phpunit.result.cache
/phpunit.xml
###< symfony/phpunit-bridge ###

###> friendsofphp/php-cs-fixer ###
!.php_cs
/.php_cs.cache
###< friendsofphp/php-cs-fixer ###

###> phpunit/phpunit ###
/phpunit.xml
.phpunit.result.cache
###< phpunit/phpunit ###

DOCUMENTATION/database/*
!DOCUMENTATION/database/database.pdf

docker/certbot
docker/*/*.env

docker-compose.yaml
composer.local.json

social.local.yaml

# V2
config.php
/file

path: .gitignore


code:
[submodule "public/assets/javascript/cropperjs"]
	path = public/assets/javascript/cropperjs
	url = https://github.com/fengyuanchen/cropperjs.git

path: .gitmodules


code:
<?php

/*
 * This document has been generated with
 * https://mlocati.github.io/php-cs-fixer-configurator/#version:2.16.1|configurator
 * you can change this configuration by importing this file.
 */
return PhpCsFixer\Config::create()
    ->setRiskyAllowed(true)
    ->setRules([
        // Each line of multi-line DocComments must have an asterisk [PSR-5] and must be aligned with the first one.
        'align_multiline_comment' => ['comment_type' => 'phpdocs_like'],
        // PHP arrays should be declared using the configured syntax.
        'array_syntax' => ['syntax' => 'short'],
        // Binary operators should be surrounded by space as configured.
        'binary_operator_spaces' => [
            'default'   => 'align_single_space_minimal',
            'operators' => ['??' => 'align'],
        ],
        // There MUST be one blank line after the namespace declaration.
        'blank_line_after_namespace' => true,
        // Each element of an array must be indented exactly once.
        'array_indentation' => true,
        // Ensure there is no code on the same line as the PHP open tag and it is followed by a blank line.
        'blank_line_after_opening_tag' => false,
        // The body of each structure MUST be enclosed by braces. Braces should be properly placed. Body of braces should be properly indented.
        'braces' => ['allow_single_line_closure' => true, 'position_after_functions_and_oop_constructs' => 'next',
            // 'allow_single_line_functions' => true, // Awaiting PR merge...
        ],
        // A single space or none should be between cast and variable.
        'cast_spaces' => true,
        // Class, trait and interface elements must be separated with one blank line.
        'class_attributes_separation' => false,
        // Whitespace around the keywords of a class, trait or interfaces definition should be one space.
        'class_definition' => ['single_item_single_line' => true, 'single_line' => true],
        // Using `isset($var) &&` multiple times should be done in one call.
        'combine_consecutive_issets' => true,
        // Calling `unset` on multiple items should be done in one call.
        'combine_consecutive_unsets' => true,
        // Remove extra spaces in a nullable typehint.
        'compact_nullable_typehint' => true,
        // Concatenation should be spaced according configuration.
        'concat_space' => ['spacing' => 'one'],
        // The PHP constants `true`, `false`, and `null` MUST be written using the correct casing.
        'constant_case' => true,
        // Equal sign in declare statement should be surrounded by spaces or not following configuration.
        'declare_equal_normalize' => ['space' => 'single'],
        // Replaces `dirname(__FILE__)` expression with equivalent `__DIR__` constant.
        'dir_constant' => true,
        // PHP code MUST use only UTF-8 without BOM (remove BOM).
        'encoding' => true,
        // Replace deprecated `ereg` regular expression functions with `preg`.
        'ereg_to_preg' => true,
        // Escape implicit backslashes in strings and heredocs to ease the understanding of which are special chars interpreted by PHP and which not.
        'escape_implicit_backslashes' => true,
        // Add curly braces to indirect variables to make them clear to understand. Requires PHP >= 7.0.
        'explicit_indirect_variable' => true,
        // Converts implicit variables into explicit ones in double-quoted strings or heredoc syntax.
        'explicit_string_variable' => true,
        // PHP code must use the long `<?php` tags or short-echo `<?=` tags and not other tag variations.
        'full_opening_tag' => true,
        // Transforms imported FQCN parameters and return types in function arguments to short version.
        'fully_qualified_strict_types' => true,
        // Spaces should be properly placed in a function declaration.
        'function_declaration' => ['closure_function_spacing' => 'one'],
        // Ensure single space between function's argument and its typehint.
        'function_typehint_space' => true,
        // Pre- or post-increment and decrement operators should be used if possible.
        'increment_style' => true,
        // Code MUST use configured indentation type.
        'indentation_type' => true,
        // All PHP files must use same line ending.
        'line_ending' => true,
        // Use `&&` and `||` logical operators instead of `and` and `or`.
        'logical_operators' => true,
        // PHP keywords MUST be in lower case.
        'lowercase_keywords' => true,
        // Class static references `self`, `static` and `parent` MUST be in lower case.
        'lowercase_static_reference' => true,
        // Magic constants should be referred to using the correct casing.
        'magic_constant_casing' => true,
        // Magic method definitions and calls must be using the correct casing.
        'magic_method_casing' => true,
        // Replaces `intval`, `floatval`, `doubleval`, `strval` and `boolval` function calls with according type casting operator.
        'modernize_types_casting' => true,
        // DocBlocks must start with two asterisks, multiline comments must start with a single asterisk, after the opening slash. Both must end with a single asterisk before the closing slash.
        'multiline_comment_opening_closing' => true,
        // Forbid multi-line whitespace before the closing semicolon or move the semicolon to the new line for chained calls.
        'multiline_whitespace_before_semicolons' => true,
        // Add leading `\` before constant invocation of internal constant to speed up resolving. Constant name match is case-sensitive, except for `null`, `false` and `true`.
        'native_constant_invocation' => false,
        // Function defined by PHP should be called using the correct casing.
        'native_function_casing' => true,
        // Add leading `\` before function invocation to speed up resolving.
        'native_function_invocation' => false,
        // Native type hints for functions should use the correct case.
        'native_function_type_declaration_casing' => true,
        // There should be no empty lines after class opening brace.
        'no_blank_lines_after_class_opening' => true,
        // There should not be blank lines between docblock and the documented element.
        'no_blank_lines_after_phpdoc' => true,
        // There must be a comment when fall-through is intentional in a non-empty case body.
        'no_break_comment' => true,
        // The closing `? >` tag MUST be omitted from files containing only PHP.
        'no_closing_tag' => true,
        // There should not be any empty comments.
        'no_empty_comment' => true,
        // There should not be empty PHPDoc blocks.
        'no_empty_phpdoc' => true,
        // Remove useless semicolon statements.
        'no_empty_statement' => true,
        // Removes extra blank lines and/or blank lines following configuration.
        'no_extra_blank_lines' => true,
        // Remove leading slashes in `use` clauses.
        'no_leading_import_slash' => true,
        // The namespace declaration line shouldn't contain leading whitespace.
        'no_leading_namespace_whitespace' => true,
        // Either language construct `print` or `echo` should be used.
        'no_mixed_echo_print' => true,
        // Operator `=>` should not be surrounded by multi-line whitespaces.
        'no_multiline_whitespace_around_double_arrow' => true,
        // Properties MUST not be explicitly initialized with `null` except when they have a type declaration (PHP 7.4).
        'no_null_property_initialization' => true,
        // Short cast `bool` using double exclamation mark should not be used.
        'no_short_bool_cast' => true,
        // Replace short-echo `<?=` with long format `<?php echo` syntax.
        'no_short_echo_tag' => true,
        // Single-line whitespace before closing semicolon are prohibited.
        'no_singleline_whitespace_before_semicolons' => true,
        // When making a method or function call, there MUST NOT be a space between the method or function name and the opening parenthesis.
        'no_spaces_after_function_name' => true,
        // There MUST NOT be spaces around offset braces.
        'no_spaces_around_offset' => true,
        // There MUST NOT be a space after the opening parenthesis. There MUST NOT be a space before the closing parenthesis.
        'no_spaces_inside_parenthesis' => true,
        // Replaces superfluous `elseif` with `if`.
        'no_superfluous_elseif' => false,
        // Remove trailing commas in list function calls.
        'no_trailing_comma_in_list_call' => true,
        // PHP single-line arrays should not have trailing comma.
        'no_trailing_comma_in_singleline_array' => true,
        // Remove trailing whitespace at the end of non-blank lines.
        'no_trailing_whitespace' => true,
        // There MUST be no trailing spaces inside comment or PHPDoc.
        'no_trailing_whitespace_in_comment' => true,
        // Removes unneeded parentheses around control statements.
        'no_unneeded_control_parentheses' => true,
        // Removes unneeded curly braces that are superfluous and aren't part of a control structure's body.
        'no_unneeded_curly_braces' => true,
        // In function arguments there must not be arguments with default values before non-default ones.
        'no_unreachable_default_argument_value' => true,
        // Variables must be set `null` instead of using `(unset)` casting.
        'no_unset_cast' => false,
        // Properties should be set to `null` instead of using `unset`.
        'no_unset_on_property' => true,
        // Unused `use` statements must be removed.
        'no_unused_imports' => true,
        // There should not be useless `else` cases.
        'no_useless_else' => false,
        // There should not be an empty `return` statement at the end of a function.
        'no_useless_return' => true,
        // In array declaration, there MUST NOT be a whitespace before each comma.
        'no_whitespace_before_comma_in_array' => true,
        // Remove trailing whitespace at the end of blank lines.
        'no_whitespace_in_blank_line' => true,
        // Remove Zero-width space (ZWSP), Non-breaking space (NBSP) and other invisible unicode symbols.
        'non_printable_character' => true,
        // Array index should always be written by using square braces.
        'normalize_index_brace' => true,
        // There should not be space before or after object `T_OBJECT_OPERATOR` `->`.
        'object_operator_without_whitespace' => true,
        // Ordering `use` statements.
        'ordered_imports' => true,
        // Orders the elements of classes/interfaces/traits.
        'ordered_class_elements' => false,
        // PHPUnit assertion method calls like `->assertSame(true, $foo)` should be written with dedicated method like `->assertTrue($foo)`.
        'php_unit_construct' => true,
        // PHPUnit annotations should be a FQCNs including a root namespace.
        'php_unit_fqcn_annotation' => true,
        // Enforce camel (or snake) case for PHPUnit test methods, following configuration.
        'php_unit_method_casing' => true,
        // Usage of PHPUnit's mock e.g. `->will($this->returnValue(..))` must be replaced by its shorter equivalent such as `->willReturn(...)`.
        'php_unit_mock_short_will_return' => true,
        // Order `@covers` annotation of PHPUnit tests.
        'php_unit_ordered_covers' => true,
        // Changes the visibility of the `setUp()` and `tearDown()` functions of PHPUnit to `protected`, to match the PHPUnit TestCase.
        'php_unit_set_up_tear_down_visibility' => true,
        // PHPUnit methods like `assertSame` should be used instead of `assertEquals`.
        'php_unit_strict' => true,
        // Calls to `PHPUnit\Framework\TestCase` static methods must all be of the same type, either `$this->`, `self::` or `static::`.
        'php_unit_test_case_static_method_calls' => true,
        // PHPDoc should contain `@param` for all params.
        'phpdoc_add_missing_param_annotation' => true,
        // All items of the given phpdoc tags must be either left-aligned or (by default) aligned vertically.
        'phpdoc_align' => true,
        // Docblocks should have the same indentation as the documented subject.
        'phpdoc_indent' => true,
        // Fix PHPDoc inline tags, make `@inheritdoc` always inline.
        'phpdoc_inline_tag' => true,
        // `@access` annotations should be omitted from PHPDoc.
        'phpdoc_no_access' => true,
        // No alias PHPDoc tags should be used.
        'phpdoc_no_alias_tag' => true,
        // Annotations in PHPDoc should be ordered so that `@param` annotations come first, then `@throws` annotations, then `@return` annotations.
        'phpdoc_order' => true,
        // The type of `@return` annotations of methods returning a reference to itself must the configured one.
        'phpdoc_return_self_reference' => true,
        // Scalar types should always be written in the same form. `int` not `integer`, `bool` not `boolean`, `float` not `real` or `double`.
        'phpdoc_scalar' => true,
        // Annotations in PHPDoc should be grouped together so that annotations of the same type immediately follow each other, and annotations of a different type are separated by a single blank line.
        'phpdoc_separation' => true,
        // Single line `@var` PHPDoc should have proper spacing.
        'phpdoc_single_line_var_spacing' => false,
        // Removes extra blank lines after summary and after description in PHPDoc.
        'phpdoc_trim_consecutive_blank_line_separation' => true,
        // The correct case must be used for standard PHP types in PHPDoc.
        'phpdoc_types' => true,
        // Sorts PHPDoc types.
        'phpdoc_types_order' => true,
        // `@var` and `@type` annotations must have type and name in the correct order.
        'phpdoc_var_annotation_correct_order' => true,
        // Class names should match the file name.
        'psr4' => true,
        // There should be one or no space before colon, and one space after it in return type declarations, according to configuration.
        'return_type_declaration' => true,
        // Inside class or interface element `self` should be preferred to the class name itself.
        'self_accessor' => true,
        // Instructions must be terminated with a semicolon.
        'semicolon_after_instruction' => true,
        // Cast shall be used, not `settype`.
        'set_type_to_cast' => true,
        // Cast `(boolean)` and `(integer)` should be written as `(bool)` and `(int)`, `(double)` and `(real)` as `(float)`, `(binary)` as `(string)`.
        'short_scalar_cast' => true,
        // Converts explicit variables in double-quoted strings and heredoc syntax from simple to complex format (`${` to `{$`).
        'simple_to_complex_string_variable' => true,
        // There should be exactly one blank line before a namespace declaration.
        'single_blank_line_before_namespace' => true,
        // There MUST be one use keyword per declaration.
        'single_import_per_statement' => true,
        // There MUST NOT be more than one property or constant declared per statement.
        'single_class_element_per_statement' => true,
        // Each namespace use MUST go on its own line and there MUST be one blank line after the use statements block.
        'single_line_after_imports' => true,
        // Single-line comments and multi-line comments with only one line of actual content should use the `//` syntax.
        'single_line_comment_style' => true,
        // Convert double quotes to single quotes for simple strings.
        'single_quote' => true,
        // Each trait `use` must be done as single statement.
        'single_trait_insert_per_statement' => true,
        // Fix whitespace after a semicolon.
        'space_after_semicolon' => true,
        // Increment and decrement operators should be used if possible.
        'standardize_increment' => true,
        // Replace all `<>` with `!=`.
        'standardize_not_equals' => true,
        // A case should be followed by a colon and not a semicolon.
        'switch_case_semicolon_to_colon' => true,
        // Removes extra spaces between colon and case value.
        'switch_case_space' => true,
        // Standardize spaces around ternary operator.
        'ternary_operator_spaces' => true,
        // PHP multi-line arrays should have a trailing comma.
        'trailing_comma_in_multiline_array' => true,
        // Unary operators should be placed adjacent to their operands.
        'unary_operator_spaces' => true,
        // Visibility MUST be declared on all properties and methods; `abstract` and `final` MUST be declared before the visibility; `static` MUST be declared after the visibility.
        'visibility_required' => true,
        // In array declaration, there MUST be a whitespace after each comma.
        'whitespace_after_comma_in_array' => true,
    ])
    ->setFinder(PhpCsFixer\Finder::create()
        ->exclude('vendor')
        ->exclude('var')
        ->exclude('docker')
        ->exclude('src/Entity')
        ->notPath('src/Core/DB/DefaultSettings.php')
        ->in(__DIR__)
    );


code:
Credits for GNU social
======================
The following is an incomplete list of developers
who've worked on GNU social, or its predecessors
StatusNet and Free Social. Apologies for any
oversight; please let mattl@gnu.org know if
anyone's been overlooked in error.

Current team
------------
* Alexei Sorokin
* Bruno Casteleiro
* Diogo Cordeiro
* Hugo Sales

Additional Contributors
-----------------------
* Akio
* Blaine Cook
* Bob Mottram
* Brenda Wallace
* Brett Taylor
* Brian Hendrickson
* Brigitte Schuster
* Ciaran Gultnieks
* Craig Andrews
* Daniel Supernault
* Dan Moore
* David Yip
* Deb Nicholson
* Donald Robertson
* Eric Helgeson
* Federico Marani
* Fil
* Garret Buell
* Henry Story
* Ian Denhart
* Jeffery To
* Jeff Mitchell
* Ken Sedgwick
* Leslie Michael Orchard
* Maiyannah Bishop
* Matthew Gregg
* Matt Lee
* mEDI
* Melvin Carvalho
* Michael Landers
* Miguel Dantas
* Mikael Nordfeldth
* Mike Cochrane
* Moonman
* Neil E Hodges
* Normandy
* Ori Avtalion
* Sean Murphy
* Stéphane Bérubé
* Steven DuBois
* Tobias Diekershoff
* Verius

Credits for StatusNet
--------------
Leads
* Evan Prodromou
* Zach Copley

Team
* Adrian Lang
* Brion Vibber
* 'drry'
* Earle Martin
* Erik Stambaugh
* Florian Biree
* Gina Haeussge
* James Walker
* Joshua Judson Rosen (rozzin)
* Ken Sheppardson
* Marie-Claude Doyon
* Meitar Moscovitz
* Ori Avtalion
* Robin Millette
* Samantha Doherty
* Sarven Capadisli
* Simon Waters, Surevine
* Tryggvi Björgvinsson

Translators
-----------
* Siebrand Mazeland
* Tiago 'gouki' Faria
* TranslateWiki.net

A special thanks to the thousands of people who
have tried out GNU social, told their friends, and
built the fediverse network to what it is today.

License help from
-----------------
* Bradley M. Kuhn


code:
### Entrypoint

GNU social's entrypoint is the `public/index.php` script, which gets
called by the webserver for all requests. This is handled by the
webserver itself, which translates a `GET /foo` to `GET
/index.php?p=foo`. This feature is called 'fancy URLs', as it was in V2.

The `index` script handles all the initialization of the Symfony
framework and social itself. It reads configuration from `.env` or any
`.env.*` file at the project root. The `index` script creates a
`Kernel` object, which is defined on the `src/Kernel.php` file. This
is the part where the code we control starts; the `Kernel` constructor
creates the needed constants, sets the timezone to UTC and the string
encoding to UTF8. The other functions in this class get called by the
Symfony framework at the appropriate times. We will come back to this
file.

### Registering services

Next, the `src/Util/GNUsocial.php` class is instantiated by the
Symfony framework, on the `'onKernelRequest'` or `'onCommand'` events. The
former event, as described in the docs:

> This event is dispatched very early in Symfony, before the
> controller is determined. It's useful to add information to the
> Request or return a Response early to stop the handling of the
> request.

The latter, is launched on the `bin/console` script is used.

In both cases, these events call the `register` function, which
creates static references for the logging, event and translation
services. This is done so these services can be used via static
function calls, which is much less verbose and more accessible than
the way the framework recommends. This function also loads all the
Modules and Plugins, which like in V2, are components that aren't
directly connected to the core code, being used to implement internal
and optional functionality respectively, by handling events launched
by various parts of the code.

### Database definitions

Going back to the `Kernel`, the `build` function gets called by the
Symfony framework and allows us to register a 'Compiler Pass'.
Specifically, we register the `SchemaDefPass` from the
`src/DependencyInjection/Compiler/SchemaDefPass.php` file, which adds
a new 'metadata driver' to Doctrine. The metadata driver is
responsible for loading database definitions. We keep the same method
as in V2, which was that each 'Entity' has a `schemaDef` static
function which returns an array describing the database.

This definition is handled by the `SchemaDefDriver` class from
`src/Util/SchemaDefDriver.php` file, which extends `StaticPHPDriver`
and replaces the methods `loadMetadata` with `schemaDef`. The function
`loadMetadataForClass` function is called by the Symfony framework for
each file in `src/Entity/`. It allows us to call the `schemaDef`
function and translate the array definition to Doctrine's internal
representation.

### Routing

Next, we'll look at the `RouteLoader`, defined in
`src/Util/RoutLoader.php`, which loads all the files from
`src/Routes/*.php` and calls the static `load` method, which defines
routes with an interface similar to V2's `connect`, except it requires
an extra identifier as the first argument. This identifier is used,
for instance, to generate URLs for each route. Each route connects an
URL path to a Controller, with the possibility of taking arguments,
which are passed to the `__invoke` method of the respective
controller. The controllers are defined in `src/Controller/` and are
responsible for handling a request and return a Symfony `Response`
object (subject to change, in order to abstract HTML vs JSON output).

### End to end

The next steps are handled by the Symfony framework which creates a
`Request` object from the HTTP request, and then a corresponding
`Response` is created by calling the `Kernel::handle` method, which
matches the appropriate route and thus calls it's controller.

### Performance

All this happens on each request, which seems like a lot to handle,
and would be too slow. Fortunately, Symfony has a 'compiler' which
caches and optimizes the code paths. In production mode, this can be
done through a command, while in development mode, it's handled on
each request if the file changed, which has a performance impact, but
obviously makes development easier.


code:
In the `dev` environment, the default values for the config table are reloaded on each HTTP request

In case you want to override this, add `SOCIAL_NO_RELOAD_DEFAULTS=1` to your .env.local file